1269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawkepackage fi.iki.elonen; 2269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 38b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie/* 48b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * #%L 58b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * NanoHttpd-Core 68b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * %% 78b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * Copyright (C) 2012 - 2015 nanohttpd 88b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * %% 98b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * Redistribution and use in source and binary forms, with or without modification, 108b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * are permitted provided that the following conditions are met: 119e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 128b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * 1. Redistributions of source code must retain the above copyright notice, this 138b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * list of conditions and the following disclaimer. 149e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 158b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * 2. Redistributions in binary form must reproduce the above copyright notice, 168b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * this list of conditions and the following disclaimer in the documentation 178b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * and/or other materials provided with the distribution. 189e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 198b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * 3. Neither the name of the nanohttpd nor the names of its contributors 208b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * may be used to endorse or promote products derived from this software without 218b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * specific prior written permission. 229e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 238b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 248b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 258b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 268b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 278b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 288b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 298b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 308b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 318b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 328b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * OF THE POSSIBILITY OF SUCH DAMAGE. 338b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie * #L% 348b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie */ 358b3f33ce61812b29a5cacd1e7e6d350b40531f9britchie 368f5edfe4109511a25ccb16f526823435284481adritchieimport java.io.BufferedInputStream; 373ea79cfc3ef8447430e6fcc9570c1551831ff9c4ritchieimport java.io.BufferedReader; 383ea79cfc3ef8447430e6fcc9570c1551831ff9c4ritchieimport java.io.BufferedWriter; 393ea79cfc3ef8447430e6fcc9570c1551831ff9c4ritchieimport java.io.ByteArrayInputStream; 403ea79cfc3ef8447430e6fcc9570c1551831ff9c4ritchieimport java.io.ByteArrayOutputStream; 413ea79cfc3ef8447430e6fcc9570c1551831ff9c4ritchieimport java.io.Closeable; 423ea79cfc3ef8447430e6fcc9570c1551831ff9c4ritchieimport java.io.DataOutput; 433ea79cfc3ef8447430e6fcc9570c1551831ff9c4ritchieimport java.io.DataOutputStream; 443ea79cfc3ef8447430e6fcc9570c1551831ff9c4ritchieimport java.io.File; 453ea79cfc3ef8447430e6fcc9570c1551831ff9c4ritchieimport java.io.FileOutputStream; 463ea79cfc3ef8447430e6fcc9570c1551831ff9c4ritchieimport java.io.FilterOutputStream; 473ea79cfc3ef8447430e6fcc9570c1551831ff9c4ritchieimport java.io.IOException; 483ea79cfc3ef8447430e6fcc9570c1551831ff9c4ritchieimport java.io.InputStream; 493ea79cfc3ef8447430e6fcc9570c1551831ff9c4ritchieimport java.io.InputStreamReader; 503ea79cfc3ef8447430e6fcc9570c1551831ff9c4ritchieimport java.io.OutputStream; 513ea79cfc3ef8447430e6fcc9570c1551831ff9c4ritchieimport java.io.OutputStreamWriter; 523ea79cfc3ef8447430e6fcc9570c1551831ff9c4ritchieimport java.io.PrintWriter; 533ea79cfc3ef8447430e6fcc9570c1551831ff9c4ritchieimport java.io.RandomAccessFile; 543ea79cfc3ef8447430e6fcc9570c1551831ff9c4ritchieimport java.io.UnsupportedEncodingException; 55b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.net.InetAddress; 56b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.net.InetSocketAddress; 57b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.net.ServerSocket; 58b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.net.Socket; 59b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.net.SocketException; 6013736e18ec88e3df74d055e061fb324e04778ad6Paul Hawkeimport java.net.SocketTimeoutException; 6114d6e72cc194ace40662bdf9d81806f14e96d212ritchieimport java.net.URL; 62b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.net.URLDecoder; 63dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawkeimport java.nio.ByteBuffer; 64dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawkeimport java.nio.channels.FileChannel; 65367e30264ff0572adbb53483a6a65cdebe406fbcritchieimport java.nio.charset.Charset; 6630fb85f55cbd8df3005e652da3781f51294baf90ritchieimport java.security.KeyStore; 677b88819e82b89ac3476ce060903468076a73de8fPaul Hawkeimport java.text.SimpleDateFormat; 68b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.ArrayList; 69b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.Calendar; 70abcf1089ce1de49278970f088883cb32acb4f225ritchieimport java.util.Collections; 71b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.Date; 7214d6e72cc194ace40662bdf9d81806f14e96d212ritchieimport java.util.Enumeration; 73b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.HashMap; 74b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.Iterator; 75b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.List; 76b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.Locale; 77b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.Map; 7814d6e72cc194ace40662bdf9d81806f14e96d212ritchieimport java.util.Properties; 79b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.StringTokenizer; 80b01d2c9086c3df2b63bd1eca602320cd7f45d24cSherif Moursiimport java.util.TimeZone; 813526d931ae635536355e46a6b242405d309c70f3ritchieimport java.util.logging.Level; 823526d931ae635536355e46a6b242405d309c70f3ritchieimport java.util.logging.Logger; 83731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchieimport java.util.regex.Matcher; 84731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchieimport java.util.regex.Pattern; 85a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theanderimport java.util.zip.GZIPOutputStream; 86269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 87cd2779c430690b6ca793d1fafdff0edb0497a686ritchieimport javax.net.ssl.KeyManager; 88cd2779c430690b6ca793d1fafdff0edb0497a686ritchieimport javax.net.ssl.KeyManagerFactory; 89cd2779c430690b6ca793d1fafdff0edb0497a686ritchieimport javax.net.ssl.SSLContext; 90cd2779c430690b6ca793d1fafdff0edb0497a686ritchieimport javax.net.ssl.SSLServerSocket; 91cd2779c430690b6ca793d1fafdff0edb0497a686ritchieimport javax.net.ssl.SSLServerSocketFactory; 92cd2779c430690b6ca793d1fafdff0edb0497a686ritchieimport javax.net.ssl.TrustManagerFactory; 93cd2779c430690b6ca793d1fafdff0edb0497a686ritchie 94cd2779c430690b6ca793d1fafdff0edb0497a686ritchieimport fi.iki.elonen.NanoHTTPD.Response.IStatus; 95cd2779c430690b6ca793d1fafdff0edb0497a686ritchieimport fi.iki.elonen.NanoHTTPD.Response.Status; 96269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 97269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke/** 985b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke * A simple, tiny, nicely embeddable HTTP server in Java 99d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 100d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 101b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke * NanoHTTPD 1029058464950a9734da0a7ff2dc47f3081bbb5117critchie * <p> 1039058464950a9734da0a7ff2dc47f3081bbb5117critchie * Copyright (c) 2012-2013 by Paul S. Hawke, 2001,2005-2013 by Jarno Elonen, 1049058464950a9734da0a7ff2dc47f3081bbb5117critchie * 2010 by Konstantinos Togias 1059058464950a9734da0a7ff2dc47f3081bbb5117critchie * </p> 106d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 107d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 108269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <b>Features + limitations: </b> 109269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <ul> 110d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 111269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Only one Java file</li> 11201ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * <li>Java 5 compatible</li> 113269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Released as open source, Modified BSD licence</li> 1149058464950a9734da0a7ff2dc47f3081bbb5117critchie * <li>No fixed config files, logging, authorization etc. (Implement yourself if 1159058464950a9734da0a7ff2dc47f3081bbb5117critchie * you need them.)</li> 1169058464950a9734da0a7ff2dc47f3081bbb5117critchie * <li>Supports parameter parsing of GET and POST methods (+ rudimentary PUT 1179058464950a9734da0a7ff2dc47f3081bbb5117critchie * support in 1.25)</li> 118269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports both dynamic content and file serving</li> 119269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports file upload (since version 1.2, 2010)</li> 120269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports partial content (streaming)</li> 121269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Supports ETags</li> 122269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Never caches anything</li> 123269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Doesn't limit bandwidth, request time or simultaneous connections</li> 124269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>Default code serves files and shows all HTTP parameters and headers</li> 125269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports directory listing, index.html and index.htm</li> 126269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports partial content (streaming)</li> 127269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports ETags</li> 128269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server does the 301 redirection trick for directories without '/'</li> 129269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server supports simple skipping for files (continue download)</li> 130269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <li>File server serves also very long files without memory overhead</li> 131cf230611ca85378ae2b0e8130dace5766edfd295Philipp Wiesemann * <li>Contains a built-in list of most common MIME types</li> 1329058464950a9734da0a7ff2dc47f3081bbb5117critchie * <li>All header names are converted to lower case so they don't vary between 1339058464950a9734da0a7ff2dc47f3081bbb5117critchie * browsers/clients</li> 134d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 135269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * </ul> 136d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 137d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 13801ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * <b>How to use: </b> 139269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * <ul> 140d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 14101ecef8887ed32be34ef7442a656a0b3330ecb8dPaul Hawke * <li>Subclass and implement serve() and embed to your own program</li> 142d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 143269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke * </ul> 144d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 1459058464950a9734da0a7ff2dc47f3081bbb5117critchie * See the separate "LICENSE.md" file for the distribution license (Modified BSD 1469058464950a9734da0a7ff2dc47f3081bbb5117critchie * licence) 147269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 148d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawkepublic abstract class NanoHTTPD { 1499058464950a9734da0a7ff2dc47f3081bbb5117critchie 1505b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke /** 1518dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke * Pluggable strategy for asynchronously executing requests. 1525b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke */ 15330fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface AsyncRunner { 154269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 155abcf1089ce1de49278970f088883cb32acb4f225ritchie void closeAll(); 156abcf1089ce1de49278970f088883cb32acb4f225ritchie 157abcf1089ce1de49278970f088883cb32acb4f225ritchie void closed(ClientHandler clientHandler); 158abcf1089ce1de49278970f088883cb32acb4f225ritchie 159abcf1089ce1de49278970f088883cb32acb4f225ritchie void exec(ClientHandler code); 160f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke } 161f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke 1625b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke /** 163abcf1089ce1de49278970f088883cb32acb4f225ritchie * The runnable that will be used for every new client connection. 1645b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke */ 165abcf1089ce1de49278970f088883cb32acb4f225ritchie public class ClientHandler implements Runnable { 1663f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke 167abcf1089ce1de49278970f088883cb32acb4f225ritchie private final InputStream inputStream; 168abcf1089ce1de49278970f088883cb32acb4f225ritchie 169abcf1089ce1de49278970f088883cb32acb4f225ritchie private final Socket acceptSocket; 170abcf1089ce1de49278970f088883cb32acb4f225ritchie 171abcf1089ce1de49278970f088883cb32acb4f225ritchie private ClientHandler(InputStream inputStream, Socket acceptSocket) { 172abcf1089ce1de49278970f088883cb32acb4f225ritchie this.inputStream = inputStream; 173abcf1089ce1de49278970f088883cb32acb4f225ritchie this.acceptSocket = acceptSocket; 1748dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke } 1758dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 176abcf1089ce1de49278970f088883cb32acb4f225ritchie public void close() { 177abcf1089ce1de49278970f088883cb32acb4f225ritchie safeClose(this.inputStream); 178abcf1089ce1de49278970f088883cb32acb4f225ritchie safeClose(this.acceptSocket); 179abcf1089ce1de49278970f088883cb32acb4f225ritchie } 180abcf1089ce1de49278970f088883cb32acb4f225ritchie 181abcf1089ce1de49278970f088883cb32acb4f225ritchie @Override 182abcf1089ce1de49278970f088883cb32acb4f225ritchie public void run() { 183abcf1089ce1de49278970f088883cb32acb4f225ritchie OutputStream outputStream = null; 184c7fd739559d0f33a7fdbc831183acc5e2b0b15bbPaul Hawke try { 185abcf1089ce1de49278970f088883cb32acb4f225ritchie outputStream = this.acceptSocket.getOutputStream(); 186abcf1089ce1de49278970f088883cb32acb4f225ritchie TempFileManager tempFileManager = NanoHTTPD.this.tempFileManagerFactory.create(); 187abcf1089ce1de49278970f088883cb32acb4f225ritchie HTTPSession session = new HTTPSession(tempFileManager, this.inputStream, outputStream, this.acceptSocket.getInetAddress()); 188abcf1089ce1de49278970f088883cb32acb4f225ritchie while (!this.acceptSocket.isClosed()) { 189abcf1089ce1de49278970f088883cb32acb4f225ritchie session.execute(); 190abcf1089ce1de49278970f088883cb32acb4f225ritchie } 191abcf1089ce1de49278970f088883cb32acb4f225ritchie } catch (Exception e) { 192abcf1089ce1de49278970f088883cb32acb4f225ritchie // When the socket is closed by the client, 193abcf1089ce1de49278970f088883cb32acb4f225ritchie // we throw our own SocketException 194abcf1089ce1de49278970f088883cb32acb4f225ritchie // to break the "keep alive" loop above. If 195abcf1089ce1de49278970f088883cb32acb4f225ritchie // the exception was anything other 196abcf1089ce1de49278970f088883cb32acb4f225ritchie // than the expected SocketException OR a 197abcf1089ce1de49278970f088883cb32acb4f225ritchie // SocketTimeoutException, print the 198abcf1089ce1de49278970f088883cb32acb4f225ritchie // stacktrace 199abcf1089ce1de49278970f088883cb32acb4f225ritchie if (!(e instanceof SocketException && "NanoHttpd Shutdown".equals(e.getMessage())) && !(e instanceof SocketTimeoutException)) { 200abcf1089ce1de49278970f088883cb32acb4f225ritchie NanoHTTPD.LOG.log(Level.FINE, "Communication with the client broken", e); 201abcf1089ce1de49278970f088883cb32acb4f225ritchie } 202abcf1089ce1de49278970f088883cb32acb4f225ritchie } finally { 203abcf1089ce1de49278970f088883cb32acb4f225ritchie safeClose(outputStream); 204abcf1089ce1de49278970f088883cb32acb4f225ritchie safeClose(this.inputStream); 205abcf1089ce1de49278970f088883cb32acb4f225ritchie safeClose(this.acceptSocket); 206abcf1089ce1de49278970f088883cb32acb4f225ritchie NanoHTTPD.this.asyncRunner.closed(this); 207c7fd739559d0f33a7fdbc831183acc5e2b0b15bbPaul Hawke } 208c7fd739559d0f33a7fdbc831183acc5e2b0b15bbPaul Hawke } 209c7fd739559d0f33a7fdbc831183acc5e2b0b15bbPaul Hawke } 210c7fd739559d0f33a7fdbc831183acc5e2b0b15bbPaul Hawke 21130fb85f55cbd8df3005e652da3781f51294baf90ritchie public static class Cookie { 2129058464950a9734da0a7ff2dc47f3081bbb5117critchie 21330fb85f55cbd8df3005e652da3781f51294baf90ritchie public static String getHTTPTime(int days) { 21430fb85f55cbd8df3005e652da3781f51294baf90ritchie Calendar calendar = Calendar.getInstance(); 21530fb85f55cbd8df3005e652da3781f51294baf90ritchie SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US); 21630fb85f55cbd8df3005e652da3781f51294baf90ritchie dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); 21730fb85f55cbd8df3005e652da3781f51294baf90ritchie calendar.add(Calendar.DAY_OF_MONTH, days); 21830fb85f55cbd8df3005e652da3781f51294baf90ritchie return dateFormat.format(calendar.getTime()); 21930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 22030fb85f55cbd8df3005e652da3781f51294baf90ritchie 22130fb85f55cbd8df3005e652da3781f51294baf90ritchie private final String n, v, e; 22230fb85f55cbd8df3005e652da3781f51294baf90ritchie 22330fb85f55cbd8df3005e652da3781f51294baf90ritchie public Cookie(String name, String value) { 22430fb85f55cbd8df3005e652da3781f51294baf90ritchie this(name, value, 30); 22530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 22630fb85f55cbd8df3005e652da3781f51294baf90ritchie 22730fb85f55cbd8df3005e652da3781f51294baf90ritchie public Cookie(String name, String value, int numDays) { 22830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.n = name; 22930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.v = value; 23030fb85f55cbd8df3005e652da3781f51294baf90ritchie this.e = getHTTPTime(numDays); 23130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 23230fb85f55cbd8df3005e652da3781f51294baf90ritchie 23330fb85f55cbd8df3005e652da3781f51294baf90ritchie public Cookie(String name, String value, String expires) { 23430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.n = name; 23530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.v = value; 23630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.e = expires; 23730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 23830fb85f55cbd8df3005e652da3781f51294baf90ritchie 23930fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getHTTPHeader() { 24030fb85f55cbd8df3005e652da3781f51294baf90ritchie String fmt = "%s=%s; expires=%s"; 24130fb85f55cbd8df3005e652da3781f51294baf90ritchie return String.format(fmt, this.n, this.v, this.e); 242c7fd739559d0f33a7fdbc831183acc5e2b0b15bbPaul Hawke } 243c7fd739559d0f33a7fdbc831183acc5e2b0b15bbPaul Hawke } 244c7fd739559d0f33a7fdbc831183acc5e2b0b15bbPaul Hawke 2453f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke /** 24630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Provides rudimentary support for cookies. Doesn't support 'path', 24730fb85f55cbd8df3005e652da3781f51294baf90ritchie * 'secure' nor 'httpOnly'. Feel free to improve it and/or add unsupported 24830fb85f55cbd8df3005e652da3781f51294baf90ritchie * features. 2499e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 25030fb85f55cbd8df3005e652da3781f51294baf90ritchie * @author LordFokas 2513f2aa2e2e0e02fe7e0151eb64d31265b578885ecPaul Hawke */ 25230fb85f55cbd8df3005e652da3781f51294baf90ritchie public class CookieHandler implements Iterable<String> { 253f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke 25430fb85f55cbd8df3005e652da3781f51294baf90ritchie private final HashMap<String, String> cookies = new HashMap<String, String>(); 2559058464950a9734da0a7ff2dc47f3081bbb5117critchie 25630fb85f55cbd8df3005e652da3781f51294baf90ritchie private final ArrayList<Cookie> queue = new ArrayList<Cookie>(); 2579058464950a9734da0a7ff2dc47f3081bbb5117critchie 25830fb85f55cbd8df3005e652da3781f51294baf90ritchie public CookieHandler(Map<String, String> httpHeaders) { 25930fb85f55cbd8df3005e652da3781f51294baf90ritchie String raw = httpHeaders.get("cookie"); 26030fb85f55cbd8df3005e652da3781f51294baf90ritchie if (raw != null) { 26130fb85f55cbd8df3005e652da3781f51294baf90ritchie String[] tokens = raw.split(";"); 26230fb85f55cbd8df3005e652da3781f51294baf90ritchie for (String token : tokens) { 26330fb85f55cbd8df3005e652da3781f51294baf90ritchie String[] data = token.trim().split("="); 26430fb85f55cbd8df3005e652da3781f51294baf90ritchie if (data.length == 2) { 26530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.cookies.put(data[0], data[1]); 2666200253df7b934c807eb3436b51a3b039406457dPaul Hawke } 26730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 268269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 26930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 270d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke 27130fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 27230fb85f55cbd8df3005e652da3781f51294baf90ritchie * Set a cookie with an expiration date from a month ago, effectively 27330fb85f55cbd8df3005e652da3781f51294baf90ritchie * deleting it on the client side. 2749e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 27530fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param name 27630fb85f55cbd8df3005e652da3781f51294baf90ritchie * The cookie name. 27730fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 27830fb85f55cbd8df3005e652da3781f51294baf90ritchie public void delete(String name) { 27930fb85f55cbd8df3005e652da3781f51294baf90ritchie set(name, "-delete-", -30); 28030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2819058464950a9734da0a7ff2dc47f3081bbb5117critchie 28230fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 28330fb85f55cbd8df3005e652da3781f51294baf90ritchie public Iterator<String> iterator() { 28430fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.cookies.keySet().iterator(); 28530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2869058464950a9734da0a7ff2dc47f3081bbb5117critchie 28730fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 28830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Read a cookie from the HTTP Headers. 2899e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 29030fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param name 29130fb85f55cbd8df3005e652da3781f51294baf90ritchie * The cookie's name. 29230fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return The cookie's value if it exists, null otherwise. 29330fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 29430fb85f55cbd8df3005e652da3781f51294baf90ritchie public String read(String name) { 29530fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.cookies.get(name); 29630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2979058464950a9734da0a7ff2dc47f3081bbb5117critchie 29830fb85f55cbd8df3005e652da3781f51294baf90ritchie public void set(Cookie cookie) { 29930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.queue.add(cookie); 30030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 3019058464950a9734da0a7ff2dc47f3081bbb5117critchie 30230fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 30330fb85f55cbd8df3005e652da3781f51294baf90ritchie * Sets a cookie. 3049e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 30530fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param name 30630fb85f55cbd8df3005e652da3781f51294baf90ritchie * The cookie's name. 30730fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param value 30830fb85f55cbd8df3005e652da3781f51294baf90ritchie * The cookie's value. 30930fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param expires 31030fb85f55cbd8df3005e652da3781f51294baf90ritchie * How many days until the cookie expires. 31130fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 31230fb85f55cbd8df3005e652da3781f51294baf90ritchie public void set(String name, String value, int expires) { 31330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.queue.add(new Cookie(name, value, Cookie.getHTTPTime(expires))); 31430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 315269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 31630fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 31730fb85f55cbd8df3005e652da3781f51294baf90ritchie * Internally used by the webserver to add all queued cookies into the 31830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Response's HTTP Headers. 3199e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 32030fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param response 32130fb85f55cbd8df3005e652da3781f51294baf90ritchie * The Response object to which headers the queued cookies 32230fb85f55cbd8df3005e652da3781f51294baf90ritchie * will be added. 32330fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 32430fb85f55cbd8df3005e652da3781f51294baf90ritchie public void unloadQueue(Response response) { 32530fb85f55cbd8df3005e652da3781f51294baf90ritchie for (Cookie cookie : this.queue) { 32630fb85f55cbd8df3005e652da3781f51294baf90ritchie response.addHeader("Set-Cookie", cookie.getHTTPHeader()); 327fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke } 328269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 329269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 330269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 3310a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander /** 33230fb85f55cbd8df3005e652da3781f51294baf90ritchie * Default threading strategy for NanoHTTPD. 33330fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 33430fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p> 33530fb85f55cbd8df3005e652da3781f51294baf90ritchie * By default, the server spawns a new Thread for every incoming request. 33630fb85f55cbd8df3005e652da3781f51294baf90ritchie * These are set to <i>daemon</i> status, and named according to the request 33730fb85f55cbd8df3005e652da3781f51294baf90ritchie * number. The name is useful when profiling the application. 33830fb85f55cbd8df3005e652da3781f51294baf90ritchie * </p> 3390a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander */ 34030fb85f55cbd8df3005e652da3781f51294baf90ritchie public static class DefaultAsyncRunner implements AsyncRunner { 3410a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander 34230fb85f55cbd8df3005e652da3781f51294baf90ritchie private long requestCount; 3430a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander 344abcf1089ce1de49278970f088883cb32acb4f225ritchie private final List<ClientHandler> running = Collections.synchronizedList(new ArrayList<NanoHTTPD.ClientHandler>()); 3458dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 346d685218eacc23e69f685a76113665f50cc560edfritchie /** 347d685218eacc23e69f685a76113665f50cc560edfritchie * @return a list with currently running clients. 348d685218eacc23e69f685a76113665f50cc560edfritchie */ 349d685218eacc23e69f685a76113665f50cc560edfritchie public List<ClientHandler> getRunning() { 350d685218eacc23e69f685a76113665f50cc560edfritchie return running; 351d685218eacc23e69f685a76113665f50cc560edfritchie } 3528dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 35330fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 354abcf1089ce1de49278970f088883cb32acb4f225ritchie public void closeAll() { 355abcf1089ce1de49278970f088883cb32acb4f225ritchie // copy of the list for concurrency 356abcf1089ce1de49278970f088883cb32acb4f225ritchie for (ClientHandler clientHandler : new ArrayList<ClientHandler>(this.running)) { 357abcf1089ce1de49278970f088883cb32acb4f225ritchie clientHandler.close(); 358abcf1089ce1de49278970f088883cb32acb4f225ritchie } 359abcf1089ce1de49278970f088883cb32acb4f225ritchie } 3608dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 361abcf1089ce1de49278970f088883cb32acb4f225ritchie @Override 362abcf1089ce1de49278970f088883cb32acb4f225ritchie public void closed(ClientHandler clientHandler) { 363abcf1089ce1de49278970f088883cb32acb4f225ritchie this.running.remove(clientHandler); 364abcf1089ce1de49278970f088883cb32acb4f225ritchie } 3658dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 366abcf1089ce1de49278970f088883cb32acb4f225ritchie @Override 367abcf1089ce1de49278970f088883cb32acb4f225ritchie public void exec(ClientHandler clientHandler) { 36830fb85f55cbd8df3005e652da3781f51294baf90ritchie ++this.requestCount; 369abcf1089ce1de49278970f088883cb32acb4f225ritchie Thread t = new Thread(clientHandler); 37030fb85f55cbd8df3005e652da3781f51294baf90ritchie t.setDaemon(true); 37130fb85f55cbd8df3005e652da3781f51294baf90ritchie t.setName("NanoHttpd Request Processor (#" + this.requestCount + ")"); 372abcf1089ce1de49278970f088883cb32acb4f225ritchie this.running.add(clientHandler); 37330fb85f55cbd8df3005e652da3781f51294baf90ritchie t.start(); 374c7fd739559d0f33a7fdbc831183acc5e2b0b15bbPaul Hawke } 37512973d7c220d261c3748e3c8051b3ef478cf5b7aDaniel Erat } 37612973d7c220d261c3748e3c8051b3ef478cf5b7aDaniel Erat 37712973d7c220d261c3748e3c8051b3ef478cf5b7aDaniel Erat /** 37830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Default strategy for creating and cleaning up temporary files. 379d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke * <p/> 38030fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p> 38130fb85f55cbd8df3005e652da3781f51294baf90ritchie * By default, files are created by <code>File.createTempFile()</code> in 38230fb85f55cbd8df3005e652da3781f51294baf90ritchie * the directory specified. 38330fb85f55cbd8df3005e652da3781f51294baf90ritchie * </p> 384269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 38530fb85f55cbd8df3005e652da3781f51294baf90ritchie public static class DefaultTempFile implements TempFile { 38630fb85f55cbd8df3005e652da3781f51294baf90ritchie 38730fb85f55cbd8df3005e652da3781f51294baf90ritchie private final File file; 38830fb85f55cbd8df3005e652da3781f51294baf90ritchie 38930fb85f55cbd8df3005e652da3781f51294baf90ritchie private final OutputStream fstream; 39030fb85f55cbd8df3005e652da3781f51294baf90ritchie 391cd2779c430690b6ca793d1fafdff0edb0497a686ritchie public DefaultTempFile(File tempdir) throws IOException { 392cd2779c430690b6ca793d1fafdff0edb0497a686ritchie this.file = File.createTempFile("NanoHTTPD-", "", tempdir); 39330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.fstream = new FileOutputStream(this.file); 3949058464950a9734da0a7ff2dc47f3081bbb5117critchie } 3959058464950a9734da0a7ff2dc47f3081bbb5117critchie 39630fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 39730fb85f55cbd8df3005e652da3781f51294baf90ritchie public void delete() throws Exception { 39830fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(this.fstream); 3996034f344ef9a9915c96616de12cfc53ce17605d4Philipp Wiesemann if (!this.file.delete()) { 4006034f344ef9a9915c96616de12cfc53ce17605d4Philipp Wiesemann throw new Exception("could not delete temporary file"); 4016034f344ef9a9915c96616de12cfc53ce17605d4Philipp Wiesemann } 4029058464950a9734da0a7ff2dc47f3081bbb5117critchie } 4039058464950a9734da0a7ff2dc47f3081bbb5117critchie 40430fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 40530fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getName() { 40630fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.file.getAbsolutePath(); 4079058464950a9734da0a7ff2dc47f3081bbb5117critchie } 4089058464950a9734da0a7ff2dc47f3081bbb5117critchie 40930fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 41030fb85f55cbd8df3005e652da3781f51294baf90ritchie public OutputStream open() throws Exception { 41130fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.fstream; 41230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 4138dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke } 4149788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 415f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke /** 41630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Default strategy for creating and cleaning up temporary files. 41793441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed * <p/> 41830fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p> 41930fb85f55cbd8df3005e652da3781f51294baf90ritchie * This class stores its files in the standard location (that is, wherever 42030fb85f55cbd8df3005e652da3781f51294baf90ritchie * <code>java.io.tmpdir</code> points to). Files are added to an internal 42130fb85f55cbd8df3005e652da3781f51294baf90ritchie * list, and deleted when no longer needed (that is, when 42230fb85f55cbd8df3005e652da3781f51294baf90ritchie * <code>clear()</code> is invoked at the end of processing a request). 42330fb85f55cbd8df3005e652da3781f51294baf90ritchie * </p> 42493441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed */ 42530fb85f55cbd8df3005e652da3781f51294baf90ritchie public static class DefaultTempFileManager implements TempFileManager { 426f39ce1dd765bb79b0a83a8e6dd921f0d3986ebf6Aaron Davidson 427cd2779c430690b6ca793d1fafdff0edb0497a686ritchie private final File tmpdir; 4289058464950a9734da0a7ff2dc47f3081bbb5117critchie 42930fb85f55cbd8df3005e652da3781f51294baf90ritchie private final List<TempFile> tempFiles; 4309058464950a9734da0a7ff2dc47f3081bbb5117critchie 43130fb85f55cbd8df3005e652da3781f51294baf90ritchie public DefaultTempFileManager() { 432cd2779c430690b6ca793d1fafdff0edb0497a686ritchie this.tmpdir = new File(System.getProperty("java.io.tmpdir")); 433cd2779c430690b6ca793d1fafdff0edb0497a686ritchie if (!tmpdir.exists()) { 434cd2779c430690b6ca793d1fafdff0edb0497a686ritchie tmpdir.mkdirs(); 4358dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke } 43630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFiles = new ArrayList<TempFile>(); 43793441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed } 43893441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 43930fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 44030fb85f55cbd8df3005e652da3781f51294baf90ritchie public void clear() { 44130fb85f55cbd8df3005e652da3781f51294baf90ritchie for (TempFile file : this.tempFiles) { 44230fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 44330fb85f55cbd8df3005e652da3781f51294baf90ritchie file.delete(); 44430fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception ignored) { 44530fb85f55cbd8df3005e652da3781f51294baf90ritchie NanoHTTPD.LOG.log(Level.WARNING, "could not delete file ", ignored); 44630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 447fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke } 44830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFiles.clear(); 449269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 450269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 45130fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 4521f2440c5f2b5b511de0e76c02d5424ddd6be1482Jarno Elonen public TempFile createTempFile(String filename_hint) throws Exception { 45330fb85f55cbd8df3005e652da3781f51294baf90ritchie DefaultTempFile tempFile = new DefaultTempFile(this.tmpdir); 45430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFiles.add(tempFile); 45530fb85f55cbd8df3005e652da3781f51294baf90ritchie return tempFile; 45630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 45793441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed } 45893441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 45993441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed /** 46030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Default strategy for creating and cleaning up temporary files. 461f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke */ 46230fb85f55cbd8df3005e652da3781f51294baf90ritchie private class DefaultTempFileManagerFactory implements TempFileManagerFactory { 4630a35219489d8f19d13197bc60eec168dd6b3089dAlbin Theander 46430fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 46530fb85f55cbd8df3005e652da3781f51294baf90ritchie public TempFileManager create() { 46630fb85f55cbd8df3005e652da3781f51294baf90ritchie return new DefaultTempFileManager(); 467f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke } 468f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke } 469f639f201819cb2652cfcad67ed8633e0609cb440Paul Hawke 470a86bc4b8d23f1d481ea7781f86b88eeb2e3e2546ritchie private static final String CHARSET_REGEX = "[ |\t]*(charset)[ |\t]*=[ |\t]*['|\"]?([^\"^'^;]*)['|\"]?"; 471a86bc4b8d23f1d481ea7781f86b88eeb2e3e2546ritchie 472a86bc4b8d23f1d481ea7781f86b88eeb2e3e2546ritchie private static final Pattern CHARSET_PATTERN = Pattern.compile(CHARSET_REGEX, Pattern.CASE_INSENSITIVE); 473a86bc4b8d23f1d481ea7781f86b88eeb2e3e2546ritchie 474a86bc4b8d23f1d481ea7781f86b88eeb2e3e2546ritchie private static final String BOUNDARY_REGEX = "[ |\t]*(boundary)[ |\t]*=[ |\t]*['|\"]?([^\"^'^;]*)['|\"]?"; 475a86bc4b8d23f1d481ea7781f86b88eeb2e3e2546ritchie 476a86bc4b8d23f1d481ea7781f86b88eeb2e3e2546ritchie private static final Pattern BOUNDARY_PATTERN = Pattern.compile(BOUNDARY_REGEX, Pattern.CASE_INSENSITIVE); 477a86bc4b8d23f1d481ea7781f86b88eeb2e3e2546ritchie 4785b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke /** 479529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf * Creates a normal ServerSocket for TCP connections 4805b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke */ 4811ea2389061205bf7872a02794f652616ab204a41hoelzlwimmerf public static class DefaultServerSocketFactory implements ServerSocketFactory { 482529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf 483529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf @Override 4844c3db1479b17ae004301a6214e9adb88040fefc4Hoeze public ServerSocket create() throws IOException { 4854c3db1479b17ae004301a6214e9adb88040fefc4Hoeze return new ServerSocket(); 486529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf } 487529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf 488f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke } 489f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke 4905b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke /** 491529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf * Creates a new SSLServerSocket 4925b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke */ 4931ea2389061205bf7872a02794f652616ab204a41hoelzlwimmerf public static class SecureServerSocketFactory implements ServerSocketFactory { 4945b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 495529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf private SSLServerSocketFactory sslServerSocketFactory; 4965b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 497529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf private String[] sslProtocols; 4985b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 499529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf public SecureServerSocketFactory(SSLServerSocketFactory sslServerSocketFactory, String[] sslProtocols) { 500529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf this.sslServerSocketFactory = sslServerSocketFactory; 501529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf this.sslProtocols = sslProtocols; 502529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf } 5038dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 504529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf @Override 5054c3db1479b17ae004301a6214e9adb88040fefc4Hoeze public ServerSocket create() throws IOException { 506529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf SSLServerSocket ss = null; 5074c3db1479b17ae004301a6214e9adb88040fefc4Hoeze ss = (SSLServerSocket) this.sslServerSocketFactory.createServerSocket(); 5084c3db1479b17ae004301a6214e9adb88040fefc4Hoeze if (this.sslProtocols != null) { 5094c3db1479b17ae004301a6214e9adb88040fefc4Hoeze ss.setEnabledProtocols(this.sslProtocols); 5104c3db1479b17ae004301a6214e9adb88040fefc4Hoeze } else { 5114c3db1479b17ae004301a6214e9adb88040fefc4Hoeze ss.setEnabledProtocols(ss.getSupportedProtocols()); 5128dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke } 5134c3db1479b17ae004301a6214e9adb88040fefc4Hoeze ss.setUseClientMode(false); 5144c3db1479b17ae004301a6214e9adb88040fefc4Hoeze ss.setWantClientAuth(false); 5154c3db1479b17ae004301a6214e9adb88040fefc4Hoeze ss.setNeedClientAuth(false); 516529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf return ss; 5178dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke } 5188dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 5198dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke } 5208dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 521731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie private static final String CONTENT_DISPOSITION_REGEX = "([ |\t]*Content-Disposition[ |\t]*:)(.*)"; 5226200253df7b934c807eb3436b51a3b039406457dPaul Hawke 523731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie private static final Pattern CONTENT_DISPOSITION_PATTERN = Pattern.compile(CONTENT_DISPOSITION_REGEX, Pattern.CASE_INSENSITIVE); 5248dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 525731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie private static final String CONTENT_TYPE_REGEX = "([ |\t]*content-type[ |\t]*:)(.*)"; 5266200253df7b934c807eb3436b51a3b039406457dPaul Hawke 527731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie private static final Pattern CONTENT_TYPE_PATTERN = Pattern.compile(CONTENT_TYPE_REGEX, Pattern.CASE_INSENSITIVE); 5286200253df7b934c807eb3436b51a3b039406457dPaul Hawke 529731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie private static final String CONTENT_DISPOSITION_ATTRIBUTE_REGEX = "[ |\t]*([a-zA-Z]*)[ |\t]*=[ |\t]*['|\"]([^\"^']*)['|\"]"; 5306200253df7b934c807eb3436b51a3b039406457dPaul Hawke 531731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie private static final Pattern CONTENT_DISPOSITION_ATTRIBUTE_PATTERN = Pattern.compile(CONTENT_DISPOSITION_ATTRIBUTE_REGEX); 5326200253df7b934c807eb3436b51a3b039406457dPaul Hawke 53330fb85f55cbd8df3005e652da3781f51294baf90ritchie protected class HTTPSession implements IHTTPSession { 5346200253df7b934c807eb3436b51a3b039406457dPaul Hawke 5354cfc55d35e82a603c677d61abdd56aca862ec148ritchie private static final int REQUEST_BUFFER_LEN = 512; 5368dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 5374cfc55d35e82a603c677d61abdd56aca862ec148ritchie private static final int MEMORY_STORE_LIMIT = 1024; 5385b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 53930fb85f55cbd8df3005e652da3781f51294baf90ritchie public static final int BUFSIZE = 8192; 5405b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 5416b302d1735579ac02f327b87cc99fdbfea09f61britchie public static final int MAX_HEADER_SIZE = 1024; 5425b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 54330fb85f55cbd8df3005e652da3781f51294baf90ritchie private final TempFileManager tempFileManager; 5445b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 54530fb85f55cbd8df3005e652da3781f51294baf90ritchie private final OutputStream outputStream; 5465b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 547eb15b339a3a688cbb3b9a0c99dc1f062a5c5276bEric Roth private final BufferedInputStream inputStream; 5485b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 54930fb85f55cbd8df3005e652da3781f51294baf90ritchie private int splitbyte; 5505b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 55130fb85f55cbd8df3005e652da3781f51294baf90ritchie private int rlen; 5525b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 55330fb85f55cbd8df3005e652da3781f51294baf90ritchie private String uri; 5545b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 55530fb85f55cbd8df3005e652da3781f51294baf90ritchie private Method method; 5565b4919a7af60ff39d1d47dea7e668fbde333dd84Paul Hawke 55730fb85f55cbd8df3005e652da3781f51294baf90ritchie private Map<String, String> parms; 5588dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 55930fb85f55cbd8df3005e652da3781f51294baf90ritchie private Map<String, String> headers; 5609788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 56130fb85f55cbd8df3005e652da3781f51294baf90ritchie private CookieHandler cookies; 5629788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 56330fb85f55cbd8df3005e652da3781f51294baf90ritchie private String queryParameterString; 5649788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 56530fb85f55cbd8df3005e652da3781f51294baf90ritchie private String remoteIp; 5668dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 567dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen private String protocolVersion; 568dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen 56930fb85f55cbd8df3005e652da3781f51294baf90ritchie public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream) { 57030fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFileManager = tempFileManager; 571eb15b339a3a688cbb3b9a0c99dc1f062a5c5276bEric Roth this.inputStream = new BufferedInputStream(inputStream, HTTPSession.BUFSIZE); 57230fb85f55cbd8df3005e652da3781f51294baf90ritchie this.outputStream = outputStream; 5739788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 574fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke 57530fb85f55cbd8df3005e652da3781f51294baf90ritchie public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream, InetAddress inetAddress) { 57630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFileManager = tempFileManager; 577eb15b339a3a688cbb3b9a0c99dc1f062a5c5276bEric Roth this.inputStream = new BufferedInputStream(inputStream, HTTPSession.BUFSIZE); 57830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.outputStream = outputStream; 57930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.remoteIp = inetAddress.isLoopbackAddress() || inetAddress.isAnyLocalAddress() ? "127.0.0.1" : inetAddress.getHostAddress().toString(); 58030fb85f55cbd8df3005e652da3781f51294baf90ritchie this.headers = new HashMap<String, String>(); 58144f52c7b241f235d887ab57bf0ffd97a8ce0f5c4yankee } 5829788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 5839788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke /** 58430fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decodes the sent headers and loads the data into Key/value pairs 5857f0727787957c2f093412c01d165846842ec2425Paul Hawke */ 58630fb85f55cbd8df3005e652da3781f51294baf90ritchie private void decodeHeader(BufferedReader in, Map<String, String> pre, Map<String, String> parms, Map<String, String> headers) throws ResponseException { 5877f0727787957c2f093412c01d165846842ec2425Paul Hawke try { 58830fb85f55cbd8df3005e652da3781f51294baf90ritchie // Read the request line 58930fb85f55cbd8df3005e652da3781f51294baf90ritchie String inLine = in.readLine(); 59030fb85f55cbd8df3005e652da3781f51294baf90ritchie if (inLine == null) { 59130fb85f55cbd8df3005e652da3781f51294baf90ritchie return; 5927f0727787957c2f093412c01d165846842ec2425Paul Hawke } 5937f0727787957c2f093412c01d165846842ec2425Paul Hawke 59430fb85f55cbd8df3005e652da3781f51294baf90ritchie StringTokenizer st = new StringTokenizer(inLine); 59530fb85f55cbd8df3005e652da3781f51294baf90ritchie if (!st.hasMoreTokens()) { 59630fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Syntax error. Usage: GET /example/file.html"); 5977f0727787957c2f093412c01d165846842ec2425Paul Hawke } 5987f0727787957c2f093412c01d165846842ec2425Paul Hawke 59930fb85f55cbd8df3005e652da3781f51294baf90ritchie pre.put("method", st.nextToken()); 6007f0727787957c2f093412c01d165846842ec2425Paul Hawke 60130fb85f55cbd8df3005e652da3781f51294baf90ritchie if (!st.hasMoreTokens()) { 60230fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Missing URI. Usage: GET /example/file.html"); 6037f0727787957c2f093412c01d165846842ec2425Paul Hawke } 6047f0727787957c2f093412c01d165846842ec2425Paul Hawke 60530fb85f55cbd8df3005e652da3781f51294baf90ritchie String uri = st.nextToken(); 6067f0727787957c2f093412c01d165846842ec2425Paul Hawke 60730fb85f55cbd8df3005e652da3781f51294baf90ritchie // Decode parameters from the URI 60830fb85f55cbd8df3005e652da3781f51294baf90ritchie int qmi = uri.indexOf('?'); 60930fb85f55cbd8df3005e652da3781f51294baf90ritchie if (qmi >= 0) { 61030fb85f55cbd8df3005e652da3781f51294baf90ritchie decodeParms(uri.substring(qmi + 1), parms); 61130fb85f55cbd8df3005e652da3781f51294baf90ritchie uri = decodePercent(uri.substring(0, qmi)); 61230fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 61330fb85f55cbd8df3005e652da3781f51294baf90ritchie uri = decodePercent(uri); 6147f0727787957c2f093412c01d165846842ec2425Paul Hawke } 615b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke 61630fb85f55cbd8df3005e652da3781f51294baf90ritchie // If there's another token, its protocol version, 617dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen // followed by HTTP headers. 61830fb85f55cbd8df3005e652da3781f51294baf90ritchie // NOTE: this now forces header names lower case since they are 61930fb85f55cbd8df3005e652da3781f51294baf90ritchie // case insensitive and vary by client. 620de2bb1bc9fa3959846741e1fd14076971e17b90cElonen if (st.hasMoreTokens()) { 621dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen protocolVersion = st.nextToken(); 622fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } else { 623dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen protocolVersion = "HTTP/1.1"; 624dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen NanoHTTPD.LOG.log(Level.FINE, "no protocol version specified, strange. Assuming HTTP/1.1."); 6257f0727787957c2f093412c01d165846842ec2425Paul Hawke } 62630fb85f55cbd8df3005e652da3781f51294baf90ritchie String line = in.readLine(); 62730fb85f55cbd8df3005e652da3781f51294baf90ritchie while (line != null && line.trim().length() > 0) { 62830fb85f55cbd8df3005e652da3781f51294baf90ritchie int p = line.indexOf(':'); 62930fb85f55cbd8df3005e652da3781f51294baf90ritchie if (p >= 0) { 63030fb85f55cbd8df3005e652da3781f51294baf90ritchie headers.put(line.substring(0, p).trim().toLowerCase(Locale.US), line.substring(p + 1).trim()); 6317f0727787957c2f093412c01d165846842ec2425Paul Hawke } 63230fb85f55cbd8df3005e652da3781f51294baf90ritchie line = in.readLine(); 6337f0727787957c2f093412c01d165846842ec2425Paul Hawke } 6347f0727787957c2f093412c01d165846842ec2425Paul Hawke 63530fb85f55cbd8df3005e652da3781f51294baf90ritchie pre.put("uri", uri); 6367f0727787957c2f093412c01d165846842ec2425Paul Hawke } catch (IOException ioe) { 63730fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage(), ioe); 6387f0727787957c2f093412c01d165846842ec2425Paul Hawke } 6397f0727787957c2f093412c01d165846842ec2425Paul Hawke } 6407f0727787957c2f093412c01d165846842ec2425Paul Hawke 64130fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 64230fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decodes the Multipart Body data and put it into Key/Value pairs. 64330fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 644a86bc4b8d23f1d481ea7781f86b88eeb2e3e2546ritchie private void decodeMultipartFormData(String boundary, String encoding, ByteBuffer fbuf, Map<String, String> parms, Map<String, String> files) throws ResponseException { 64530fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 6463cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen int[] boundary_idxs = getBoundaryPositions(fbuf, boundary.getBytes()); 6473cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen if (boundary_idxs.length < 2) { 6483cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but contains less than two boundary strings."); 6493cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen } 6503cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen 6515e6d76fe9c6bf67e7cdeb6e310952af639614030Jarno Elonen byte[] part_header_buff = new byte[MAX_HEADER_SIZE]; 6523cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen for (int bi = 0; bi < boundary_idxs.length - 1; bi++) { 6533cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen fbuf.position(boundary_idxs[bi]); 6545e6d76fe9c6bf67e7cdeb6e310952af639614030Jarno Elonen int len = (fbuf.remaining() < MAX_HEADER_SIZE) ? fbuf.remaining() : MAX_HEADER_SIZE; 6553cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen fbuf.get(part_header_buff, 0, len); 6566b302d1735579ac02f327b87cc99fdbfea09f61britchie BufferedReader in = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(part_header_buff, 0, len), Charset.forName(encoding)), len); 6573cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen 6586b302d1735579ac02f327b87cc99fdbfea09f61britchie int headerLines = 0; 6593cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen // First line is boundary string 6603cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen String mpline = in.readLine(); 6616b302d1735579ac02f327b87cc99fdbfea09f61britchie headerLines++; 66230fb85f55cbd8df3005e652da3781f51294baf90ritchie if (!mpline.contains(boundary)) { 6633cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but chunk does not start with boundary."); 66430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 6653cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen 6663cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen String part_name = null, file_name = null, content_type = null; 6673cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen // Parse the reset of the header lines 66830fb85f55cbd8df3005e652da3781f51294baf90ritchie mpline = in.readLine(); 6696b302d1735579ac02f327b87cc99fdbfea09f61britchie headerLines++; 67030fb85f55cbd8df3005e652da3781f51294baf90ritchie while (mpline != null && mpline.trim().length() > 0) { 671731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie Matcher matcher = CONTENT_DISPOSITION_PATTERN.matcher(mpline); 672731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie if (matcher.matches()) { 673731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie String attributeString = matcher.group(2); 674731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie matcher = CONTENT_DISPOSITION_ATTRIBUTE_PATTERN.matcher(attributeString); 675731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie while (matcher.find()) { 676731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie String key = matcher.group(1); 677731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie if (key.equalsIgnoreCase("name")) { 678731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie part_name = matcher.group(2); 679731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie } else if (key.equalsIgnoreCase("filename")) { 680731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie file_name = matcher.group(2); 68130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 68230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 6833cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen } 684731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie matcher = CONTENT_TYPE_PATTERN.matcher(mpline); 685731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie if (matcher.matches()) { 686731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie content_type = matcher.group(2).trim(); 687731ecb0ecb91cc31d3f12a657c4a34b40fa18f3fritchie } 6883cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen mpline = in.readLine(); 6896b302d1735579ac02f327b87cc99fdbfea09f61britchie headerLines++; 6906b302d1735579ac02f327b87cc99fdbfea09f61britchie } 6916b302d1735579ac02f327b87cc99fdbfea09f61britchie int part_header_len = 0; 6926b302d1735579ac02f327b87cc99fdbfea09f61britchie while (headerLines-- > 0) { 6936b302d1735579ac02f327b87cc99fdbfea09f61britchie part_header_len = scipOverNewLine(part_header_buff, part_header_len); 6943cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen } 6953cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen // Read the part data 6963cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen if (part_header_len >= len - 4) { 6975e6d76fe9c6bf67e7cdeb6e310952af639614030Jarno Elonen throw new ResponseException(Response.Status.INTERNAL_ERROR, "Multipart header size exceeds MAX_HEADER_SIZE."); 6983cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen } 6993cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen int part_data_start = boundary_idxs[bi] + part_header_len; 7003cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen int part_data_end = boundary_idxs[bi + 1] - 4; 7013cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen 7023cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen fbuf.position(part_data_start); 7033cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen if (content_type == null) { 7043cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen // Read the part into a string 7053cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen byte[] data_bytes = new byte[part_data_end - part_data_start]; 7063cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen fbuf.get(data_bytes); 707a86bc4b8d23f1d481ea7781f86b88eeb2e3e2546ritchie parms.put(part_name, new String(data_bytes, encoding)); 7083cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen } else { 7093cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen // Read it into a file 7101f2440c5f2b5b511de0e76c02d5424ddd6be1482Jarno Elonen String path = saveTmpFile(fbuf, part_data_start, part_data_end - part_data_start, file_name); 7113cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen if (!files.containsKey(part_name)) { 7123cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen files.put(part_name, path); 71330fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 7143cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen int count = 2; 7153cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen while (files.containsKey(part_name + count)) { 7163cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen count++; 71730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 7183cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen files.put(part_name + count, path); 71930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 7203cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen parms.put(part_name, file_name); 72198d6d146214026ab242ce1de320bd7d203d7a5f3Dan Pomeroy } 72298d6d146214026ab242ce1de320bd7d203d7a5f3Dan Pomeroy } 7233cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen } catch (ResponseException re) { 7243cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen throw re; 7253cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen } catch (Exception e) { 7263cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen throw new ResponseException(Response.Status.INTERNAL_ERROR, e.toString()); 727c6c080a6f4377fb18fc869111714e9514c36331aPaul Hawke } 728fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke } 729fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke 7306b302d1735579ac02f327b87cc99fdbfea09f61britchie private int scipOverNewLine(byte[] part_header_buff, int index) { 7316b302d1735579ac02f327b87cc99fdbfea09f61britchie while (part_header_buff[index] != '\n') { 7326b302d1735579ac02f327b87cc99fdbfea09f61britchie index++; 733c6c080a6f4377fb18fc869111714e9514c36331aPaul Hawke } 7346b302d1735579ac02f327b87cc99fdbfea09f61britchie return ++index; 735c6c080a6f4377fb18fc869111714e9514c36331aPaul Hawke } 736c6c080a6f4377fb18fc869111714e9514c36331aPaul Hawke 73730fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 73830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decodes parameters in percent-encoded URI-format ( e.g. 73930fb85f55cbd8df3005e652da3781f51294baf90ritchie * "name=Jack%20Daniels&pass=Single%20Malt" ) and adds them to given 74030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Map. NOTE: this doesn't support multiple identical keys due to the 74130fb85f55cbd8df3005e652da3781f51294baf90ritchie * simplicity of Map. 74230fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 74330fb85f55cbd8df3005e652da3781f51294baf90ritchie private void decodeParms(String parms, Map<String, String> p) { 74430fb85f55cbd8df3005e652da3781f51294baf90ritchie if (parms == null) { 74530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.queryParameterString = ""; 74630fb85f55cbd8df3005e652da3781f51294baf90ritchie return; 747fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke } 748fe8be79fa8d8b131d5d39e77ae0baabd177d5ec1Paul Hawke 74930fb85f55cbd8df3005e652da3781f51294baf90ritchie this.queryParameterString = parms; 75030fb85f55cbd8df3005e652da3781f51294baf90ritchie StringTokenizer st = new StringTokenizer(parms, "&"); 75130fb85f55cbd8df3005e652da3781f51294baf90ritchie while (st.hasMoreTokens()) { 75230fb85f55cbd8df3005e652da3781f51294baf90ritchie String e = st.nextToken(); 75330fb85f55cbd8df3005e652da3781f51294baf90ritchie int sep = e.indexOf('='); 75430fb85f55cbd8df3005e652da3781f51294baf90ritchie if (sep >= 0) { 75530fb85f55cbd8df3005e652da3781f51294baf90ritchie p.put(decodePercent(e.substring(0, sep)).trim(), decodePercent(e.substring(sep + 1))); 75630fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 75730fb85f55cbd8df3005e652da3781f51294baf90ritchie p.put(decodePercent(e).trim(), ""); 75830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 759fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } 760fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } 761fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel 76230fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 76330fb85f55cbd8df3005e652da3781f51294baf90ritchie public void execute() throws IOException { 764bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite Response r = null; 76530fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 76630fb85f55cbd8df3005e652da3781f51294baf90ritchie // Read the first 8192 bytes. 76730fb85f55cbd8df3005e652da3781f51294baf90ritchie // The full header should fit in here. 76830fb85f55cbd8df3005e652da3781f51294baf90ritchie // Apache's default header limit is 8KB. 76930fb85f55cbd8df3005e652da3781f51294baf90ritchie // Do NOT assume that a single read will get the entire header 77030fb85f55cbd8df3005e652da3781f51294baf90ritchie // at once! 77130fb85f55cbd8df3005e652da3781f51294baf90ritchie byte[] buf = new byte[HTTPSession.BUFSIZE]; 77230fb85f55cbd8df3005e652da3781f51294baf90ritchie this.splitbyte = 0; 77330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.rlen = 0; 7749c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie 7759c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie int read = -1; 776eb15b339a3a688cbb3b9a0c99dc1f062a5c5276bEric Roth this.inputStream.mark(HTTPSession.BUFSIZE); 7779c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie try { 7789c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie read = this.inputStream.read(buf, 0, HTTPSession.BUFSIZE); 7799c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie } catch (Exception e) { 7809c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie safeClose(this.inputStream); 7819c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie safeClose(this.outputStream); 7829c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie throw new SocketException("NanoHttpd Shutdown"); 7839c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie } 7849c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie if (read == -1) { 7859c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie // socket was been closed 7869c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie safeClose(this.inputStream); 7879c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie safeClose(this.outputStream); 7889c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie throw new SocketException("NanoHttpd Shutdown"); 7899c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie } 7909c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie while (read > 0) { 7919c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie this.rlen += read; 7929c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie this.splitbyte = findHeaderEnd(buf, this.rlen); 7939c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie if (this.splitbyte > 0) { 7949c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie break; 795fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } 7969c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie read = this.inputStream.read(buf, this.rlen, HTTPSession.BUFSIZE - this.rlen); 797fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } 798fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel 79930fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.splitbyte < this.rlen) { 800eb15b339a3a688cbb3b9a0c99dc1f062a5c5276bEric Roth this.inputStream.reset(); 801eb15b339a3a688cbb3b9a0c99dc1f062a5c5276bEric Roth this.inputStream.skip(this.splitbyte); 80230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 803c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 80430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.parms = new HashMap<String, String>(); 80530fb85f55cbd8df3005e652da3781f51294baf90ritchie if (null == this.headers) { 80630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.headers = new HashMap<String, String>(); 80730fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 80830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.headers.clear(); 80930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 810c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 81130fb85f55cbd8df3005e652da3781f51294baf90ritchie // Create a BufferedReader for parsing the header. 81230fb85f55cbd8df3005e652da3781f51294baf90ritchie BufferedReader hin = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf, 0, this.rlen))); 813c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 81430fb85f55cbd8df3005e652da3781f51294baf90ritchie // Decode the header into parms and header java properties 81530fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> pre = new HashMap<String, String>(); 81630fb85f55cbd8df3005e652da3781f51294baf90ritchie decodeHeader(hin, pre, this.parms, this.headers); 817c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 8180f5ae0751dcaf3182ba094403debaf085b3ac58eYibai Zhang if (null != this.remoteIp) { 8190f5ae0751dcaf3182ba094403debaf085b3ac58eYibai Zhang this.headers.put("remote-addr", this.remoteIp); 8200f5ae0751dcaf3182ba094403debaf085b3ac58eYibai Zhang this.headers.put("http-client-ip", this.remoteIp); 8210f5ae0751dcaf3182ba094403debaf085b3ac58eYibai Zhang } 822c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 82330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.method = Method.lookup(pre.get("method")); 82430fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.method == null) { 82530fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Syntax error."); 82630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 827c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 82830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.uri = pre.get("uri"); 829c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 83030fb85f55cbd8df3005e652da3781f51294baf90ritchie this.cookies = new CookieHandler(this.headers); 831c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke 832dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen String connection = this.headers.get("connection"); 833dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen boolean keepAlive = protocolVersion.equals("HTTP/1.1") && (connection == null || !connection.matches("(?i).*close.*")); 834517987ff0fe07af253c4cf6163ee5b9da17faf64SleekWeasel 83530fb85f55cbd8df3005e652da3781f51294baf90ritchie // Ok, now do the serve() 8360760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen 8370760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen // TODO: long body_size = getBodySize(); 8380760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen // TODO: long pos_before_serve = this.inputStream.totalRead() 8390760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen // (requires implementaion for totalRead()) 840bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite r = serve(this); 8410760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen // TODO: this.inputStream.skip(body_size - 8420760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen // (this.inputStream.totalRead() - pos_before_serve)) 8430760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen 84430fb85f55cbd8df3005e652da3781f51294baf90ritchie if (r == null) { 84530fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new ResponseException(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: Serve() returned a null response."); 84630fb85f55cbd8df3005e652da3781f51294baf90ritchie } else { 847a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander String acceptEncoding = this.headers.get("accept-encoding"); 84830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.cookies.unloadQueue(r); 84930fb85f55cbd8df3005e652da3781f51294baf90ritchie r.setRequestMethod(this.method); 850ddb266b3d98e6430cd892b62525d2b5eee1473b7Jarno Elonen r.setGzipEncoding(useGzipWhenAccepted(r) && acceptEncoding != null && acceptEncoding.contains("gzip")); 851dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen r.setKeepAlive(keepAlive); 85230fb85f55cbd8df3005e652da3781f51294baf90ritchie r.send(this.outputStream); 85330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 854dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen if (!keepAlive || "close".equalsIgnoreCase(r.getHeader("connection"))) { 855dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen throw new SocketException("NanoHttpd Shutdown"); 856dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen } 85730fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (SocketException e) { 85830fb85f55cbd8df3005e652da3781f51294baf90ritchie // throw it out to close socket object (finalAccept) 85930fb85f55cbd8df3005e652da3781f51294baf90ritchie throw e; 86030fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (SocketTimeoutException ste) { 86130fb85f55cbd8df3005e652da3781f51294baf90ritchie // treat socket timeouts the same way we treat socket exceptions 86230fb85f55cbd8df3005e652da3781f51294baf90ritchie // i.e. close the stream & finalAccept object by throwing the 86330fb85f55cbd8df3005e652da3781f51294baf90ritchie // exception up the call stack. 86430fb85f55cbd8df3005e652da3781f51294baf90ritchie throw ste; 86530fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (IOException ioe) { 866bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite Response resp = newFixedLengthResponse(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage()); 867bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite resp.send(this.outputStream); 86830fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(this.outputStream); 86930fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (ResponseException re) { 870bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite Response resp = newFixedLengthResponse(re.getStatus(), NanoHTTPD.MIME_PLAINTEXT, re.getMessage()); 871bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite resp.send(this.outputStream); 87230fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(this.outputStream); 87330fb85f55cbd8df3005e652da3781f51294baf90ritchie } finally { 874bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite safeClose(r); 87530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFileManager.clear(); 876fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } 87767e24e482b132ff0a8618f96b43097c9ce5f2776yankee } 878517987ff0fe07af253c4cf6163ee5b9da17faf64SleekWeasel 8797f0727787957c2f093412c01d165846842ec2425Paul Hawke /** 88030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Find byte index separating header from body. It must be the last byte 88130fb85f55cbd8df3005e652da3781f51294baf90ritchie * of the first two sequential new lines. 8829788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke */ 88330fb85f55cbd8df3005e652da3781f51294baf90ritchie private int findHeaderEnd(final byte[] buf, int rlen) { 88430fb85f55cbd8df3005e652da3781f51294baf90ritchie int splitbyte = 0; 885529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf while (splitbyte + 1 < rlen) { 8868d6a97830abf9685bc0724c24766c8f9704aacd2Christopher Lane 887529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf // RFC2616 888529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf if (buf[splitbyte] == '\r' && buf[splitbyte + 1] == '\n' && splitbyte + 3 < rlen && buf[splitbyte + 2] == '\r' && buf[splitbyte + 3] == '\n') { 88930fb85f55cbd8df3005e652da3781f51294baf90ritchie return splitbyte + 4; 890fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } 8919788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 892529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf // tolerance 893529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf if (buf[splitbyte] == '\n' && buf[splitbyte + 1] == '\n') { 894529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf return splitbyte + 2; 895529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf } 89630fb85f55cbd8df3005e652da3781f51294baf90ritchie splitbyte++; 8979788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 89830fb85f55cbd8df3005e652da3781f51294baf90ritchie return 0; 899fe1961e39809c90a76422332e91af67a5c65c31cSleekWeasel } 9009788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 90130fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 902af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen * Find the byte positions where multipart boundaries start. This reads 903af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen * a large block at a time and uses a temporary buffer to optimize 904af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen * (memory mapped) file access. 90530fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 90630fb85f55cbd8df3005e652da3781f51294baf90ritchie private int[] getBoundaryPositions(ByteBuffer b, byte[] boundary) { 907af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen int[] res = new int[0]; 908af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen if (b.remaining() < boundary.length) { 909af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen return res; 9109788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 9119788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke 912af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen int search_window_pos = 0; 913af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen byte[] search_window = new byte[4 * 1024 + boundary.length]; 914269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 915af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen int first_fill = (b.remaining() < search_window.length) ? b.remaining() : search_window.length; 916af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen b.get(search_window, 0, first_fill); 917af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen int new_bytes = first_fill - boundary.length; 9188dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 919af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen do { 920af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen // Search the search_window 921af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen for (int j = 0; j < new_bytes; j++) { 922af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen for (int i = 0; i < boundary.length; i++) { 923af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen if (search_window[j + i] != boundary[i]) 924af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen break; 925af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen if (i == boundary.length - 1) { 926af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen // Match found, add it to results 927af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen int[] new_res = new int[res.length + 1]; 928af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen System.arraycopy(res, 0, new_res, 0, res.length); 929af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen new_res[res.length] = search_window_pos + j; 930af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen res = new_res; 931af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen } 93230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 93330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 934af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen search_window_pos += new_bytes; 9358dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 936af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen // Copy the end of the buffer to the start 937af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen System.arraycopy(search_window, search_window.length - boundary.length, search_window, 0, boundary.length); 9388dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 939af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen // Refill search_window 940af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen new_bytes = search_window.length - boundary.length; 941af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen new_bytes = (b.remaining() < new_bytes) ? b.remaining() : new_bytes; 942af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen b.get(search_window, boundary.length, new_bytes); 943af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen } while (new_bytes > 0); 944af7a20b9d426d7b5e21eeacc855bf0a7386adb74Jarno Elonen return res; 9458dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke } 9468dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 94730fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 94830fb85f55cbd8df3005e652da3781f51294baf90ritchie public CookieHandler getCookies() { 94930fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.cookies; 9508dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke } 9518dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 9528dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke @Override 95330fb85f55cbd8df3005e652da3781f51294baf90ritchie public final Map<String, String> getHeaders() { 95430fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.headers; 9558dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke } 9568dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 95730fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 95830fb85f55cbd8df3005e652da3781f51294baf90ritchie public final InputStream getInputStream() { 95930fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.inputStream; 960d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke } 961d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke 96230fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 96330fb85f55cbd8df3005e652da3781f51294baf90ritchie public final Method getMethod() { 96430fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.method; 9657e4e4ae652c755faea626f1e2538d495f96e648esynapticloop } 9667e4e4ae652c755faea626f1e2538d495f96e648esynapticloop 967bc0de3e38a4edadd6704a6c8ccb0c5ee738b8f54SleekWeasel @Override 96830fb85f55cbd8df3005e652da3781f51294baf90ritchie public final Map<String, String> getParms() { 96930fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.parms; 970c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 97193441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 97230fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 97330fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getQueryParameterString() { 97430fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.queryParameterString; 975c6f3f15bbf5a0ccbff5a4f17e53804c0d3888446Paul Hawke } 97693441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 97730fb85f55cbd8df3005e652da3781f51294baf90ritchie private RandomAccessFile getTmpBucket() { 97830fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 9791f2440c5f2b5b511de0e76c02d5424ddd6be1482Jarno Elonen TempFile tempFile = this.tempFileManager.createTempFile(null); 98030fb85f55cbd8df3005e652da3781f51294baf90ritchie return new RandomAccessFile(tempFile.getName(), "rw"); 98130fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { 98230fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new Error(e); // we won't recover, so throw an error 9839788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 9849788c5b7900b4c7ff27355f69fee4e14630eb412Paul Hawke } 9858dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 986bc0de3e38a4edadd6704a6c8ccb0c5ee738b8f54SleekWeasel @Override 98730fb85f55cbd8df3005e652da3781f51294baf90ritchie public final String getUri() { 98830fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.uri; 98993441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed } 99093441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 9910760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen /** 9920760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen * Deduce body length in bytes. Either from "content-length" header or 9930760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen * read bytes. 9940760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen */ 9950760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen public long getBodySize() { 9960760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen if (this.headers.containsKey("content-length")) { 997e6b3d8f2f742d16a2ce91f573740a4aa727fc0b1Philipp Wiesemann return Long.parseLong(this.headers.get("content-length")); 9980760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen } else if (this.splitbyte < this.rlen) { 9990760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen return this.rlen - this.splitbyte; 100093441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed } 10010760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen return 0; 100293441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed } 100393441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 1004bc0de3e38a4edadd6704a6c8ccb0c5ee738b8f54SleekWeasel @Override 1005bc0de3e38a4edadd6704a6c8ccb0c5ee738b8f54SleekWeasel public void parseBody(Map<String, String> files) throws IOException, ResponseException { 100693441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed RandomAccessFile randomAccessFile = null; 100793441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed try { 10080760946bcdaf5b41827c9aa2fcfae4326b788265Jarno Elonen long size = getBodySize(); 10096625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen ByteArrayOutputStream baos = null; 10106625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen DataOutput request_data_output = null; 10116625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen 10126625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen // Store the request in memory or a file, depending on size 1013770aaf0c6567d8b62a6eef18c8f0359208427c77Jarno Elonen if (size < MEMORY_STORE_LIMIT) { 10146625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen baos = new ByteArrayOutputStream(); 10156625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen request_data_output = new DataOutputStream(baos); 1016745614b606ebcca5f69f5c84bde1774ca2f11cc5Martin M Reed } else { 10176625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen randomAccessFile = getTmpBucket(); 10186625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen request_data_output = randomAccessFile; 10190cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } 1020d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke 10216625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen // Read all the body and write it to request_data_output 10226625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen byte[] buf = new byte[REQUEST_BUFFER_LEN]; 102330fb85f55cbd8df3005e652da3781f51294baf90ritchie while (this.rlen >= 0 && size > 0) { 10246625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen this.rlen = this.inputStream.read(buf, 0, (int) Math.min(size, REQUEST_BUFFER_LEN)); 102530fb85f55cbd8df3005e652da3781f51294baf90ritchie size -= this.rlen; 102630fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.rlen > 0) { 10276625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen request_data_output.write(buf, 0, this.rlen); 10280cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke } 1029269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1030269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 10316625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen ByteBuffer fbuf = null; 10326625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen if (baos != null) { 10336625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen fbuf = ByteBuffer.wrap(baos.toByteArray(), 0, baos.size()); 10346625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen } else { 10356625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen fbuf = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, randomAccessFile.length()); 10366625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen randomAccessFile.seek(0); 10376625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen } 1038269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 1039269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // If the method is POST, there may be parameters 1040269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // in data section, too, read it: 104130fb85f55cbd8df3005e652da3781f51294baf90ritchie if (Method.POST.equals(this.method)) { 1042269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String contentType = ""; 104330fb85f55cbd8df3005e652da3781f51294baf90ritchie String contentTypeHeader = this.headers.get("content-type"); 10442b54eda5fee8430268a99d73b061e790362db544Tom Hermann 10452c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke StringTokenizer st = null; 10462c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke if (contentTypeHeader != null) { 10472c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke st = new StringTokenizer(contentTypeHeader, ",; "); 10482c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke if (st.hasMoreTokens()) { 10492c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke contentType = st.nextToken(); 10502c48103a2fc2ef4ccbd5d441da96dc77c7736109Paul Hawke } 1051269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1052269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 10530277bbd03983f15110fcf0d039a3e276d0808e29Paul Hawke if ("multipart/form-data".equalsIgnoreCase(contentType)) { 1054269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke // Handle multipart/form-data 10550cb0846c31d1eaf3fcd5d2382ca595abfb2dda38Paul Hawke if (!st.hasMoreTokens()) { 10569058464950a9734da0a7ff2dc47f3081bbb5117critchie throw new ResponseException(Response.Status.BAD_REQUEST, 10579058464950a9734da0a7ff2dc47f3081bbb5117critchie "BAD REQUEST: Content type is multipart/form-data but boundary missing. Usage: GET /example/file.html"); 10584e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke } 1059a86bc4b8d23f1d481ea7781f86b88eeb2e3e2546ritchie decodeMultipartFormData(getAttributeFromContentHeader(contentTypeHeader, BOUNDARY_PATTERN, null), // 1060a86bc4b8d23f1d481ea7781f86b88eeb2e3e2546ritchie getAttributeFromContentHeader(contentTypeHeader, CHARSET_PATTERN, "US-ASCII"), fbuf, this.parms, files); 1061269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } else { 10623cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen byte[] postBytes = new byte[fbuf.remaining()]; 10633cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen fbuf.get(postBytes); 10643cbc1769b317f45ad257f33bd648ac97b595f84bJarno Elonen String postLine = new String(postBytes).trim(); 1065e9a1b5310ed98d7c80095fbefd856a0b1277a1d2unknown // Handle application/x-www-form-urlencoded 1066e9a1b5310ed98d7c80095fbefd856a0b1277a1d2unknown if ("application/x-www-form-urlencoded".equalsIgnoreCase(contentType)) { 106730fb85f55cbd8df3005e652da3781f51294baf90ritchie decodeParms(postLine, this.parms); 1068e9a1b5310ed98d7c80095fbefd856a0b1277a1d2unknown } else if (postLine.length() != 0) { 10699058464950a9734da0a7ff2dc47f3081bbb5117critchie // Special case for raw POST data => create a 10709058464950a9734da0a7ff2dc47f3081bbb5117critchie // special files entry "postData" with raw content 10719058464950a9734da0a7ff2dc47f3081bbb5117critchie // data 10729058464950a9734da0a7ff2dc47f3081bbb5117critchie files.put("postData", postLine); 1073e9a1b5310ed98d7c80095fbefd856a0b1277a1d2unknown } 1074269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 107530fb85f55cbd8df3005e652da3781f51294baf90ritchie } else if (Method.PUT.equals(this.method)) { 10761f2440c5f2b5b511de0e76c02d5424ddd6be1482Jarno Elonen files.put("content", saveTmpFile(fbuf, 0, fbuf.limit(), null)); 1077b814145cf66e94b7ce5511077ca0ebcff6e67268Paul Hawke } 10784e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke } finally { 10799cd5d3b4438667394ab98895c75b4e1a2f8e76a0Martin M Reed safeClose(randomAccessFile); 1080269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1081269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1082269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 1083a86bc4b8d23f1d481ea7781f86b88eeb2e3e2546ritchie private String getAttributeFromContentHeader(String contentTypeHeader, Pattern pattern, String defaultValue) { 1084a86bc4b8d23f1d481ea7781f86b88eeb2e3e2546ritchie Matcher matcher = pattern.matcher(contentTypeHeader); 1085a86bc4b8d23f1d481ea7781f86b88eeb2e3e2546ritchie return matcher.find() ? matcher.group(2) : defaultValue; 1086a86bc4b8d23f1d481ea7781f86b88eeb2e3e2546ritchie } 1087a86bc4b8d23f1d481ea7781f86b88eeb2e3e2546ritchie 1088269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 108930fb85f55cbd8df3005e652da3781f51294baf90ritchie * Retrieves the content of a sent file and saves it to a temporary 109030fb85f55cbd8df3005e652da3781f51294baf90ritchie * file. The full path to the saved file is returned. 1091d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 10921f2440c5f2b5b511de0e76c02d5424ddd6be1482Jarno Elonen private String saveTmpFile(ByteBuffer b, int offset, int len, String filename_hint) { 1093269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke String path = ""; 1094269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke if (len > 0) { 10956b018cee02f3517742880f80c293f5ae0a905b87Paul Hawke FileOutputStream fileOutputStream = null; 1096269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke try { 10971f2440c5f2b5b511de0e76c02d5424ddd6be1482Jarno Elonen TempFile tempFile = this.tempFileManager.createTempFile(filename_hint); 1098dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke ByteBuffer src = b.duplicate(); 10996b018cee02f3517742880f80c293f5ae0a905b87Paul Hawke fileOutputStream = new FileOutputStream(tempFile.getName()); 11006b018cee02f3517742880f80c293f5ae0a905b87Paul Hawke FileChannel dest = fileOutputStream.getChannel(); 1101dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke src.position(offset).limit(offset + len); 1102dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke dest.write(src.slice()); 11034e1ae19daee9a8e9437476af6b07a6aba950802cPaul Hawke path = tempFile.getName(); 1104269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } catch (Exception e) { // Catch exception if any 1105ad3ccf027ec060e1adb3c9b6c577a8ebf32866b2yankee throw new Error(e); // we won't recover, so throw an error 1106062b50e32b22f4c419bb4b8e3d781b9eac056380Martin M Reed } finally { 11076b018cee02f3517742880f80c293f5ae0a905b87Paul Hawke safeClose(fileOutputStream); 1108269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1109269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1110269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke return path; 1111269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 111230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 1113269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 111430fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 111530fb85f55cbd8df3005e652da3781f51294baf90ritchie * Handles one session, i.e. parses the HTTP request and returns the 111630fb85f55cbd8df3005e652da3781f51294baf90ritchie * response. 111730fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 111830fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface IHTTPSession { 1119269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 112030fb85f55cbd8df3005e652da3781f51294baf90ritchie void execute() throws IOException; 1121269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 112230fb85f55cbd8df3005e652da3781f51294baf90ritchie CookieHandler getCookies(); 1123269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 112430fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> getHeaders(); 1125269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 112630fb85f55cbd8df3005e652da3781f51294baf90ritchie InputStream getInputStream(); 112730fb85f55cbd8df3005e652da3781f51294baf90ritchie 112830fb85f55cbd8df3005e652da3781f51294baf90ritchie Method getMethod(); 112930fb85f55cbd8df3005e652da3781f51294baf90ritchie 113030fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> getParms(); 113130fb85f55cbd8df3005e652da3781f51294baf90ritchie 113230fb85f55cbd8df3005e652da3781f51294baf90ritchie String getQueryParameterString(); 1133dfea30a36949bc24a698ac39d8a714fc8f1c9e82Paul Hawke 1134269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 113530fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return the path part of the URL. 1136269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 113730fb85f55cbd8df3005e652da3781f51294baf90ritchie String getUri(); 1138269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 1139269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 114030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Adds the files in the request body to the files map. 11419e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 114230fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param files 114330fb85f55cbd8df3005e652da3781f51294baf90ritchie * map to modify 1144269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 114530fb85f55cbd8df3005e652da3781f51294baf90ritchie void parseBody(Map<String, String> files) throws IOException, ResponseException; 114630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 1147269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 114830fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 114930fb85f55cbd8df3005e652da3781f51294baf90ritchie * HTTP Request methods, with the ability to decode a <code>String</code> 115030fb85f55cbd8df3005e652da3781f51294baf90ritchie * back to its enum value. 115130fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 115230fb85f55cbd8df3005e652da3781f51294baf90ritchie public enum Method { 115330fb85f55cbd8df3005e652da3781f51294baf90ritchie GET, 115430fb85f55cbd8df3005e652da3781f51294baf90ritchie PUT, 115530fb85f55cbd8df3005e652da3781f51294baf90ritchie POST, 115630fb85f55cbd8df3005e652da3781f51294baf90ritchie DELETE, 115730fb85f55cbd8df3005e652da3781f51294baf90ritchie HEAD, 11585fe20c61344a9d1654bda69d9c0af38466c8639aJarno Elonen OPTIONS, 11595fe20c61344a9d1654bda69d9c0af38466c8639aJarno Elonen TRACE, 11605fe20c61344a9d1654bda69d9c0af38466c8639aJarno Elonen CONNECT, 11615fe20c61344a9d1654bda69d9c0af38466c8639aJarno Elonen PATCH; 116230fb85f55cbd8df3005e652da3781f51294baf90ritchie 116330fb85f55cbd8df3005e652da3781f51294baf90ritchie static Method lookup(String method) { 116430fb85f55cbd8df3005e652da3781f51294baf90ritchie for (Method m : Method.values()) { 116530fb85f55cbd8df3005e652da3781f51294baf90ritchie if (m.toString().equalsIgnoreCase(method)) { 116630fb85f55cbd8df3005e652da3781f51294baf90ritchie return m; 1167269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1168269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 116930fb85f55cbd8df3005e652da3781f51294baf90ritchie return null; 1170269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 117130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 117293441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 117330fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 117430fb85f55cbd8df3005e652da3781f51294baf90ritchie * HTTP response. Return one of these from serve(). 117530fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 1176cd2779c430690b6ca793d1fafdff0edb0497a686ritchie public static class Response implements Closeable { 117793441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 117830fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface IStatus { 1179fbe92e0eb77710addafab0d48e7be5e147f17d00Paul Hawke 118030fb85f55cbd8df3005e652da3781f51294baf90ritchie String getDescription(); 118193441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 118230fb85f55cbd8df3005e652da3781f51294baf90ritchie int getRequestStatus(); 118393441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed } 118493441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 1185269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 118630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Some HTTP response status codes 1187d22b56d68169cbf5fcf9ddfb30b54fb5f155cc40Paul Hawke */ 118830fb85f55cbd8df3005e652da3781f51294baf90ritchie public enum Status implements IStatus { 118930fb85f55cbd8df3005e652da3781f51294baf90ritchie SWITCH_PROTOCOL(101, "Switching Protocols"), 119030fb85f55cbd8df3005e652da3781f51294baf90ritchie OK(200, "OK"), 119130fb85f55cbd8df3005e652da3781f51294baf90ritchie CREATED(201, "Created"), 119230fb85f55cbd8df3005e652da3781f51294baf90ritchie ACCEPTED(202, "Accepted"), 119330fb85f55cbd8df3005e652da3781f51294baf90ritchie NO_CONTENT(204, "No Content"), 119430fb85f55cbd8df3005e652da3781f51294baf90ritchie PARTIAL_CONTENT(206, "Partial Content"), 119530fb85f55cbd8df3005e652da3781f51294baf90ritchie REDIRECT(301, "Moved Permanently"), 1196f47b11cc8e8e27f27d1772c18f09135a948dd59fDaniel Erat TEMPORARY_REDIRECT(302, "Moved Temporarily"), 119730fb85f55cbd8df3005e652da3781f51294baf90ritchie NOT_MODIFIED(304, "Not Modified"), 119830fb85f55cbd8df3005e652da3781f51294baf90ritchie BAD_REQUEST(400, "Bad Request"), 119930fb85f55cbd8df3005e652da3781f51294baf90ritchie UNAUTHORIZED(401, "Unauthorized"), 120030fb85f55cbd8df3005e652da3781f51294baf90ritchie FORBIDDEN(403, "Forbidden"), 120130fb85f55cbd8df3005e652da3781f51294baf90ritchie NOT_FOUND(404, "Not Found"), 120230fb85f55cbd8df3005e652da3781f51294baf90ritchie METHOD_NOT_ALLOWED(405, "Method Not Allowed"), 1203c8aca9cd463e64f46f0fb3435b53a544205af62celonen NOT_ACCEPTABLE(406, "Not Acceptable"), 120412e6c0c5f70a6e2120ce03579884172d40093542luctrudeau REQUEST_TIMEOUT(408, "Request Timeout"), 1205c8aca9cd463e64f46f0fb3435b53a544205af62celonen CONFLICT(409, "Conflict"), 120630fb85f55cbd8df3005e652da3781f51294baf90ritchie RANGE_NOT_SATISFIABLE(416, "Requested Range Not Satisfiable"), 1207de2bb1bc9fa3959846741e1fd14076971e17b90cElonen INTERNAL_ERROR(500, "Internal Server Error"), 1208c8aca9cd463e64f46f0fb3435b53a544205af62celonen NOT_IMPLEMENTED(501, "Not Implemented"), 1209de2bb1bc9fa3959846741e1fd14076971e17b90cElonen UNSUPPORTED_HTTP_VERSION(505, "HTTP Version Not Supported"); 12108cdc631dca8d05f64ea8da466ecfe759a2f6e429Martin M Reed 121130fb85f55cbd8df3005e652da3781f51294baf90ritchie private final int requestStatus; 1212964393fd7e5ed49088882a126cf82507184467efMartin M Reed 121330fb85f55cbd8df3005e652da3781f51294baf90ritchie private final String description; 1214964393fd7e5ed49088882a126cf82507184467efMartin M Reed 121530fb85f55cbd8df3005e652da3781f51294baf90ritchie Status(int requestStatus, String description) { 121630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.requestStatus = requestStatus; 121730fb85f55cbd8df3005e652da3781f51294baf90ritchie this.description = description; 1218269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 12195820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 122030fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 122130fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getDescription() { 122230fb85f55cbd8df3005e652da3781f51294baf90ritchie return "" + this.requestStatus + " " + this.description; 122330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 12245820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 122530fb85f55cbd8df3005e652da3781f51294baf90ritchie @Override 122630fb85f55cbd8df3005e652da3781f51294baf90ritchie public int getRequestStatus() { 122730fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.requestStatus; 1228269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 12295820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 12305820f308631a825a05feddddd0aafb962f272b9bPaul Hawke } 12315820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 1232a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander /** 1233a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * Output stream that will automatically send every write to the wrapped 1234a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * OutputStream according to chunked transfer: 1235a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1 1236a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander */ 1237a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander private static class ChunkedOutputStream extends FilterOutputStream { 12385820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 1239a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander public ChunkedOutputStream(OutputStream out) { 1240a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander super(out); 1241a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 12425820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 1243a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander @Override 1244a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander public void write(int b) throws IOException { 1245a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander byte[] data = { 1246a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander (byte) b 1247a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander }; 1248a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander write(data, 0, 1); 1249a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 12508dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke 1251a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander @Override 1252a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander public void write(byte[] b) throws IOException { 1253a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander write(b, 0, b.length); 1254a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1255a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1256a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander @Override 1257a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander public void write(byte[] b, int off, int len) throws IOException { 1258a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander if (len == 0) 1259a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander return; 1260a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander out.write(String.format("%x\r\n", len).getBytes()); 1261a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander out.write(b, off, len); 1262a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander out.write("\r\n".getBytes()); 1263a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1264a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1265a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander public void finish() throws IOException { 1266a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander out.write("0\r\n\r\n".getBytes()); 12678dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke } 1268964393fd7e5ed49088882a126cf82507184467efMartin M Reed 12695820f308631a825a05feddddd0aafb962f272b9bPaul Hawke } 12705820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 12718dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke /** 127230fb85f55cbd8df3005e652da3781f51294baf90ritchie * HTTP status code after processing, e.g. "200 OK", Status.OK 12738dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke */ 127430fb85f55cbd8df3005e652da3781f51294baf90ritchie private IStatus status; 1275964393fd7e5ed49088882a126cf82507184467efMartin M Reed 12768dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke /** 127730fb85f55cbd8df3005e652da3781f51294baf90ritchie * MIME type of content, e.g. "text/html" 12788dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke */ 127930fb85f55cbd8df3005e652da3781f51294baf90ritchie private String mimeType; 1280964393fd7e5ed49088882a126cf82507184467efMartin M Reed 12818dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke /** 128230fb85f55cbd8df3005e652da3781f51294baf90ritchie * Data of the response, may be null. 12838dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke */ 128430fb85f55cbd8df3005e652da3781f51294baf90ritchie private InputStream data; 128530fb85f55cbd8df3005e652da3781f51294baf90ritchie 12869e1ec7bff40d70d31953a04dd448665aaf549395ritchie private long contentLength; 12879cd5d3b4438667394ab98895c75b4e1a2f8e76a0Martin M Reed 12888dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke /** 128930fb85f55cbd8df3005e652da3781f51294baf90ritchie * Headers for the HTTP response. Use addHeader() to add lines. 12908dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke */ 129130fb85f55cbd8df3005e652da3781f51294baf90ritchie private final Map<String, String> header = new HashMap<String, String>(); 129230fb85f55cbd8df3005e652da3781f51294baf90ritchie 129330fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 129430fb85f55cbd8df3005e652da3781f51294baf90ritchie * The request method that spawned this response. 129530fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 129630fb85f55cbd8df3005e652da3781f51294baf90ritchie private Method requestMethod; 129730fb85f55cbd8df3005e652da3781f51294baf90ritchie 129830fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 129930fb85f55cbd8df3005e652da3781f51294baf90ritchie * Use chunkedTransfer 130030fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 130130fb85f55cbd8df3005e652da3781f51294baf90ritchie private boolean chunkedTransfer; 130230fb85f55cbd8df3005e652da3781f51294baf90ritchie 1303a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander private boolean encodeAsGzip; 1304a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1305dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen private boolean keepAlive; 1306dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen 130730fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 1308dec699357b2281a580023c6c564612b7b89c5fe8Jarno Elonen * Creates a fixed length response if totalBytes>=0, otherwise chunked. 1309dec699357b2281a580023c6c564612b7b89c5fe8Jarno Elonen */ 13109e1ec7bff40d70d31953a04dd448665aaf549395ritchie protected Response(IStatus status, String mimeType, InputStream data, long totalBytes) { 1311dec699357b2281a580023c6c564612b7b89c5fe8Jarno Elonen this.status = status; 1312dec699357b2281a580023c6c564612b7b89c5fe8Jarno Elonen this.mimeType = mimeType; 13135e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie if (data == null) { 13145e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie this.data = new ByteArrayInputStream(new byte[0]); 13159e1ec7bff40d70d31953a04dd448665aaf549395ritchie this.contentLength = 0L; 13165e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } else { 13175e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie this.data = data; 13185e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie this.contentLength = totalBytes; 1319292a62aa22a550d783484e46d9c4442a153d6d69Jarno Elonen } 13205e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie this.chunkedTransfer = this.contentLength < 0; 1321dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen keepAlive = true; 1322269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1323269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 1324bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite @Override 1325bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite public void close() throws IOException { 1326bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite if (this.data != null) { 1327bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite this.data.close(); 1328bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite } 1329bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite } 1330bfcedb5f2464c6f9cbbf634c0d8be407e47680b5Meteorite 1331269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke /** 133230fb85f55cbd8df3005e652da3781f51294baf90ritchie * Adds given line to the header. 1333269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke */ 133430fb85f55cbd8df3005e652da3781f51294baf90ritchie public void addHeader(String name, String value) { 133530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.header.put(name, value); 133630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 133730fb85f55cbd8df3005e652da3781f51294baf90ritchie 133830fb85f55cbd8df3005e652da3781f51294baf90ritchie public InputStream getData() { 133930fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.data; 134030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 134130fb85f55cbd8df3005e652da3781f51294baf90ritchie 134230fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getHeader(String name) { 1343dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen for (String headerName : header.keySet()) { 1344dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen if (headerName.equalsIgnoreCase(name)) { 1345dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen return header.get(headerName); 1346dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen } 1347dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen } 1348dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen return null; 134930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 135030fb85f55cbd8df3005e652da3781f51294baf90ritchie 135130fb85f55cbd8df3005e652da3781f51294baf90ritchie public String getMimeType() { 135230fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.mimeType; 135330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 135430fb85f55cbd8df3005e652da3781f51294baf90ritchie 135530fb85f55cbd8df3005e652da3781f51294baf90ritchie public Method getRequestMethod() { 135630fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.requestMethod; 135730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 135830fb85f55cbd8df3005e652da3781f51294baf90ritchie 135930fb85f55cbd8df3005e652da3781f51294baf90ritchie public IStatus getStatus() { 136030fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.status; 136130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 136230fb85f55cbd8df3005e652da3781f51294baf90ritchie 1363a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander public void setGzipEncoding(boolean encodeAsGzip) { 1364a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander this.encodeAsGzip = encodeAsGzip; 1365a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1366a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1367dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen public void setKeepAlive(boolean useKeepAlive) { 1368dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen this.keepAlive = useKeepAlive; 1369dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen } 1370dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen 1371d06acf2b280ff10d396647e3f0f0efe4a65c74afJarno Elonen private static boolean headerAlreadySent(Map<String, String> header, String name) { 137230fb85f55cbd8df3005e652da3781f51294baf90ritchie boolean alreadySent = false; 137330fb85f55cbd8df3005e652da3781f51294baf90ritchie for (String headerName : header.keySet()) { 137430fb85f55cbd8df3005e652da3781f51294baf90ritchie alreadySent |= headerName.equalsIgnoreCase(name); 1375f29e141959a4bbb738453c3462518e52ebb218cePaul Hawke } 137630fb85f55cbd8df3005e652da3781f51294baf90ritchie return alreadySent; 137730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 1378269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke 137930fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 138030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Sends given response to the socket. 138130fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 138230fb85f55cbd8df3005e652da3781f51294baf90ritchie protected void send(OutputStream outputStream) { 138330fb85f55cbd8df3005e652da3781f51294baf90ritchie String mime = this.mimeType; 138430fb85f55cbd8df3005e652da3781f51294baf90ritchie SimpleDateFormat gmtFrmt = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US); 138530fb85f55cbd8df3005e652da3781f51294baf90ritchie gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT")); 138630fb85f55cbd8df3005e652da3781f51294baf90ritchie 138730fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 138830fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.status == null) { 138930fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new Error("sendResponse(): Status can't be null."); 139030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 13915e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream, "UTF-8")), false); 139230fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("HTTP/1.1 " + this.status.getDescription() + " \r\n"); 139330fb85f55cbd8df3005e652da3781f51294baf90ritchie 139430fb85f55cbd8df3005e652da3781f51294baf90ritchie if (mime != null) { 139530fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("Content-Type: " + mime + "\r\n"); 139630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 139730fb85f55cbd8df3005e652da3781f51294baf90ritchie 139830fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.header == null || this.header.get("Date") == null) { 139930fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("Date: " + gmtFrmt.format(new Date()) + "\r\n"); 140030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 140130fb85f55cbd8df3005e652da3781f51294baf90ritchie 140230fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.header != null) { 140330fb85f55cbd8df3005e652da3781f51294baf90ritchie for (String key : this.header.keySet()) { 140430fb85f55cbd8df3005e652da3781f51294baf90ritchie String value = this.header.get(key); 140530fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print(key + ": " + value + "\r\n"); 140630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 140730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 140830fb85f55cbd8df3005e652da3781f51294baf90ritchie 1409dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen if (!headerAlreadySent(header, "connection")) { 1410dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen pw.print("Connection: " + (this.keepAlive ? "keep-alive" : "close") + "\r\n"); 1411dde976d5b0ef7d237e32679bbe1a1a717bd0007eJarno Elonen } 141230fb85f55cbd8df3005e652da3781f51294baf90ritchie 1413a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander if (headerAlreadySent(this.header, "content-length")) { 1414a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander encodeAsGzip = false; 1415a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1416a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1417a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander if (encodeAsGzip) { 1418a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander pw.print("Content-Encoding: gzip\r\n"); 1419f713632b4a7ad3fa9c7581dff3df6a5abf7de395kira setChunkedTransfer(true); 1420a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1421a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1422a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander long pending = this.data != null ? this.contentLength : 0; 142330fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.requestMethod != Method.HEAD && this.chunkedTransfer) { 1424a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander pw.print("Transfer-Encoding: chunked\r\n"); 1425a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } else if (!encodeAsGzip) { 142630fb85f55cbd8df3005e652da3781f51294baf90ritchie pending = sendContentLengthHeaderIfNotAlreadyPresent(pw, this.header, pending); 1427269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1428a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander pw.print("\r\n"); 1429a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander pw.flush(); 1430a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander sendBodyWithCorrectTransferAndEncoding(outputStream, pending); 143130fb85f55cbd8df3005e652da3781f51294baf90ritchie outputStream.flush(); 143230fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(this.data); 143330fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (IOException ioe) { 143430fb85f55cbd8df3005e652da3781f51294baf90ritchie NanoHTTPD.LOG.log(Level.SEVERE, "Could not send response to the client", ioe); 1435269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 1436269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke } 143793441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 1438a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander private void sendBodyWithCorrectTransferAndEncoding(OutputStream outputStream, long pending) throws IOException { 1439a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander if (this.requestMethod != Method.HEAD && this.chunkedTransfer) { 1440a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander ChunkedOutputStream chunkedOutputStream = new ChunkedOutputStream(outputStream); 1441a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander sendBodyWithCorrectEncoding(chunkedOutputStream, -1); 1442a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander chunkedOutputStream.finish(); 1443a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } else { 1444a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander sendBodyWithCorrectEncoding(outputStream, pending); 144530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 144693441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed } 144793441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 1448a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander private void sendBodyWithCorrectEncoding(OutputStream outputStream, long pending) throws IOException { 1449a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander if (encodeAsGzip) { 1450882f385eacea73737f9b0bb0d7c772e80b8dc63aPhilipp Wiesemann GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream); 1451a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander sendBody(gzipOutputStream, -1); 1452a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander gzipOutputStream.finish(); 1453a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } else { 1454a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander sendBody(outputStream, pending); 1455a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1456a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1457a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander 1458a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander /** 1459a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * Sends the body to the specified OutputStream. The pending parameter 1460a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * limits the maximum amounts of bytes sent unless it is -1, in which 1461a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * case everything is sent. 1462a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * 1463a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * @param outputStream 1464a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * the OutputStream to send data to 1465a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * @param pending 1466a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * -1 to send everything, otherwise sets a max limit to the 1467a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * number of bytes sent 1468a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * @throws IOException 1469a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander * if something goes wrong while sending the data. 1470a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander */ 1471a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander private void sendBody(OutputStream outputStream, long pending) throws IOException { 1472a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander long BUFFER_SIZE = 16 * 1024; 1473a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander byte[] buff = new byte[(int) BUFFER_SIZE]; 1474a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander boolean sendEverything = pending == -1; 1475a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander while (pending > 0 || sendEverything) { 1476a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander long bytesToRead = sendEverything ? BUFFER_SIZE : Math.min(pending, BUFFER_SIZE); 1477a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander int read = this.data.read(buff, 0, (int) bytesToRead); 1478a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander if (read <= 0) { 1479a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander break; 1480a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander } 1481a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander outputStream.write(buff, 0, read); 1482a45eb0e96d8081f67baca79660ba44aa01978b88Albin Theander if (!sendEverything) { 148330fb85f55cbd8df3005e652da3781f51294baf90ritchie pending -= read; 148430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 148530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 1486fbe92e0eb77710addafab0d48e7be5e147f17d00Paul Hawke } 1487fbe92e0eb77710addafab0d48e7be5e147f17d00Paul Hawke 1488d06acf2b280ff10d396647e3f0f0efe4a65c74afJarno Elonen protected static long sendContentLengthHeaderIfNotAlreadyPresent(PrintWriter pw, Map<String, String> header, long size) { 148930fb85f55cbd8df3005e652da3781f51294baf90ritchie for (String headerName : header.keySet()) { 149030fb85f55cbd8df3005e652da3781f51294baf90ritchie if (headerName.equalsIgnoreCase("content-length")) { 149130fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 14929e1ec7bff40d70d31953a04dd448665aaf549395ritchie return Long.parseLong(header.get(headerName)); 149330fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (NumberFormatException ex) { 149430fb85f55cbd8df3005e652da3781f51294baf90ritchie return size; 149530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 149630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 149730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 149830fb85f55cbd8df3005e652da3781f51294baf90ritchie 149930fb85f55cbd8df3005e652da3781f51294baf90ritchie pw.print("Content-Length: " + size + "\r\n"); 150030fb85f55cbd8df3005e652da3781f51294baf90ritchie return size; 150130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 150230fb85f55cbd8df3005e652da3781f51294baf90ritchie 150330fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setChunkedTransfer(boolean chunkedTransfer) { 150430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.chunkedTransfer = chunkedTransfer; 150530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 150630fb85f55cbd8df3005e652da3781f51294baf90ritchie 150730fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setData(InputStream data) { 150830fb85f55cbd8df3005e652da3781f51294baf90ritchie this.data = data; 150930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 151030fb85f55cbd8df3005e652da3781f51294baf90ritchie 151130fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setMimeType(String mimeType) { 151230fb85f55cbd8df3005e652da3781f51294baf90ritchie this.mimeType = mimeType; 151330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 151430fb85f55cbd8df3005e652da3781f51294baf90ritchie 151530fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setRequestMethod(Method requestMethod) { 151630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.requestMethod = requestMethod; 151730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 151830fb85f55cbd8df3005e652da3781f51294baf90ritchie 151930fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setStatus(IStatus status) { 152030fb85f55cbd8df3005e652da3781f51294baf90ritchie this.status = status; 152130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 152230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 152330fb85f55cbd8df3005e652da3781f51294baf90ritchie 152430fb85f55cbd8df3005e652da3781f51294baf90ritchie public static final class ResponseException extends Exception { 152530fb85f55cbd8df3005e652da3781f51294baf90ritchie 152630fb85f55cbd8df3005e652da3781f51294baf90ritchie private static final long serialVersionUID = 6569838532917408380L; 152730fb85f55cbd8df3005e652da3781f51294baf90ritchie 152830fb85f55cbd8df3005e652da3781f51294baf90ritchie private final Response.Status status; 152930fb85f55cbd8df3005e652da3781f51294baf90ritchie 153030fb85f55cbd8df3005e652da3781f51294baf90ritchie public ResponseException(Response.Status status, String message) { 153130fb85f55cbd8df3005e652da3781f51294baf90ritchie super(message); 153230fb85f55cbd8df3005e652da3781f51294baf90ritchie this.status = status; 153330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 153430fb85f55cbd8df3005e652da3781f51294baf90ritchie 153530fb85f55cbd8df3005e652da3781f51294baf90ritchie public ResponseException(Response.Status status, String message, Exception e) { 153630fb85f55cbd8df3005e652da3781f51294baf90ritchie super(message, e); 153730fb85f55cbd8df3005e652da3781f51294baf90ritchie this.status = status; 153830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 153930fb85f55cbd8df3005e652da3781f51294baf90ritchie 154030fb85f55cbd8df3005e652da3781f51294baf90ritchie public Response.Status getStatus() { 154130fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.status; 154230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 154330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 154430fb85f55cbd8df3005e652da3781f51294baf90ritchie 154530fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 1546abcf1089ce1de49278970f088883cb32acb4f225ritchie * The runnable that will be used for the main listening thread. 1547abcf1089ce1de49278970f088883cb32acb4f225ritchie */ 1548abcf1089ce1de49278970f088883cb32acb4f225ritchie public class ServerRunnable implements Runnable { 1549abcf1089ce1de49278970f088883cb32acb4f225ritchie 1550abcf1089ce1de49278970f088883cb32acb4f225ritchie private final int timeout; 1551abcf1089ce1de49278970f088883cb32acb4f225ritchie 1552284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie private IOException bindException; 1553284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie 1554284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie private boolean hasBinded = false; 1555284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie 1556abcf1089ce1de49278970f088883cb32acb4f225ritchie private ServerRunnable(int timeout) { 1557abcf1089ce1de49278970f088883cb32acb4f225ritchie this.timeout = timeout; 1558abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1559abcf1089ce1de49278970f088883cb32acb4f225ritchie 1560abcf1089ce1de49278970f088883cb32acb4f225ritchie @Override 1561abcf1089ce1de49278970f088883cb32acb4f225ritchie public void run() { 1562284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie try { 1563284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie myServerSocket.bind(hostname != null ? new InetSocketAddress(hostname, myPort) : new InetSocketAddress(myPort)); 1564284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie hasBinded = true; 1565284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie } catch (IOException e) { 1566284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie this.bindException = e; 1567284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie return; 1568284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie } 1569abcf1089ce1de49278970f088883cb32acb4f225ritchie do { 1570abcf1089ce1de49278970f088883cb32acb4f225ritchie try { 1571abcf1089ce1de49278970f088883cb32acb4f225ritchie final Socket finalAccept = NanoHTTPD.this.myServerSocket.accept(); 1572901af189ee7a1a7a7dc65cd1eb60a107b2faa4a7ritchie if (this.timeout > 0) { 1573901af189ee7a1a7a7dc65cd1eb60a107b2faa4a7ritchie finalAccept.setSoTimeout(this.timeout); 1574901af189ee7a1a7a7dc65cd1eb60a107b2faa4a7ritchie } 1575abcf1089ce1de49278970f088883cb32acb4f225ritchie final InputStream inputStream = finalAccept.getInputStream(); 1576abcf1089ce1de49278970f088883cb32acb4f225ritchie NanoHTTPD.this.asyncRunner.exec(createClientHandler(finalAccept, inputStream)); 1577abcf1089ce1de49278970f088883cb32acb4f225ritchie } catch (IOException e) { 1578abcf1089ce1de49278970f088883cb32acb4f225ritchie NanoHTTPD.LOG.log(Level.FINE, "Communication with the client broken", e); 1579abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1580abcf1089ce1de49278970f088883cb32acb4f225ritchie } while (!NanoHTTPD.this.myServerSocket.isClosed()); 1581abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1582abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1583abcf1089ce1de49278970f088883cb32acb4f225ritchie 1584abcf1089ce1de49278970f088883cb32acb4f225ritchie /** 158530fb85f55cbd8df3005e652da3781f51294baf90ritchie * A temp file. 158630fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 158730fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p> 158830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Temp files are responsible for managing the actual temporary storage and 158930fb85f55cbd8df3005e652da3781f51294baf90ritchie * cleaning themselves up when no longer needed. 159030fb85f55cbd8df3005e652da3781f51294baf90ritchie * </p> 159130fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 159230fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface TempFile { 159330fb85f55cbd8df3005e652da3781f51294baf90ritchie 1594529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf public void delete() throws Exception; 159530fb85f55cbd8df3005e652da3781f51294baf90ritchie 1596529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf public String getName(); 159730fb85f55cbd8df3005e652da3781f51294baf90ritchie 1598529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf public OutputStream open() throws Exception; 159930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 160030fb85f55cbd8df3005e652da3781f51294baf90ritchie 160130fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 160230fb85f55cbd8df3005e652da3781f51294baf90ritchie * Temp file manager. 160330fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 160430fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p> 160530fb85f55cbd8df3005e652da3781f51294baf90ritchie * Temp file managers are created 1-to-1 with incoming requests, to create 160630fb85f55cbd8df3005e652da3781f51294baf90ritchie * and cleanup temporary files created as a result of handling the request. 160730fb85f55cbd8df3005e652da3781f51294baf90ritchie * </p> 160830fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 160930fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface TempFileManager { 161030fb85f55cbd8df3005e652da3781f51294baf90ritchie 161130fb85f55cbd8df3005e652da3781f51294baf90ritchie void clear(); 161230fb85f55cbd8df3005e652da3781f51294baf90ritchie 1613529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf public TempFile createTempFile(String filename_hint) throws Exception; 161430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 161530fb85f55cbd8df3005e652da3781f51294baf90ritchie 161630fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 161730fb85f55cbd8df3005e652da3781f51294baf90ritchie * Factory to create temp file managers. 161830fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 161930fb85f55cbd8df3005e652da3781f51294baf90ritchie public interface TempFileManagerFactory { 162030fb85f55cbd8df3005e652da3781f51294baf90ritchie 1621529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf public TempFileManager create(); 1622529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf } 1623529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf 1624529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf /** 1625529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf * Factory to create ServerSocketFactories. 1626529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf */ 1627529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf public interface ServerSocketFactory { 1628529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf 16294c3db1479b17ae004301a6214e9adb88040fefc4Hoeze public ServerSocket create() throws IOException; 1630529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf 163130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 163230fb85f55cbd8df3005e652da3781f51294baf90ritchie 163330fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 163430fb85f55cbd8df3005e652da3781f51294baf90ritchie * Maximum time to wait on Socket.getInputStream().read() (in milliseconds) 163530fb85f55cbd8df3005e652da3781f51294baf90ritchie * This is required as the Keep-Alive HTTP connections would otherwise block 163630fb85f55cbd8df3005e652da3781f51294baf90ritchie * the socket reading thread forever (or as long the browser is open). 163730fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 163830fb85f55cbd8df3005e652da3781f51294baf90ritchie public static final int SOCKET_READ_TIMEOUT = 5000; 163930fb85f55cbd8df3005e652da3781f51294baf90ritchie 164030fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 164130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Common MIME type for dynamic content: plain text 164230fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 164330fb85f55cbd8df3005e652da3781f51294baf90ritchie public static final String MIME_PLAINTEXT = "text/plain"; 164430fb85f55cbd8df3005e652da3781f51294baf90ritchie 164530fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 164630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Common MIME type for dynamic content: html 164730fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 164830fb85f55cbd8df3005e652da3781f51294baf90ritchie public static final String MIME_HTML = "text/html"; 164930fb85f55cbd8df3005e652da3781f51294baf90ritchie 165030fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 165130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pseudo-Parameter to use to store the actual query string in the 165230fb85f55cbd8df3005e652da3781f51294baf90ritchie * parameters map for later re-processing. 165330fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 165430fb85f55cbd8df3005e652da3781f51294baf90ritchie private static final String QUERY_STRING_PARAMETER = "NanoHttpd.QUERY_STRING"; 165530fb85f55cbd8df3005e652da3781f51294baf90ritchie 165630fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 165730fb85f55cbd8df3005e652da3781f51294baf90ritchie * logger to log to. 165830fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 16599c4e5f6d83e3a039caf3fec36372424e796e25e7ritchie private static final Logger LOG = Logger.getLogger(NanoHTTPD.class.getName()); 166030fb85f55cbd8df3005e652da3781f51294baf90ritchie 166130fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 166214d6e72cc194ace40662bdf9d81806f14e96d212ritchie * Hashtable mapping (String)FILENAME_EXTENSION -> (String)MIME_TYPE 166314d6e72cc194ace40662bdf9d81806f14e96d212ritchie */ 166414d6e72cc194ace40662bdf9d81806f14e96d212ritchie protected static Map<String, String> MIME_TYPES; 166514d6e72cc194ace40662bdf9d81806f14e96d212ritchie 166614d6e72cc194ace40662bdf9d81806f14e96d212ritchie public static Map<String, String> mimeTypes() { 166714d6e72cc194ace40662bdf9d81806f14e96d212ritchie if (MIME_TYPES == null) { 166814d6e72cc194ace40662bdf9d81806f14e96d212ritchie MIME_TYPES = new HashMap<String, String>(); 166914d6e72cc194ace40662bdf9d81806f14e96d212ritchie loadMimeTypes(MIME_TYPES, "META-INF/nanohttpd/default-mimetypes.properties"); 167014d6e72cc194ace40662bdf9d81806f14e96d212ritchie loadMimeTypes(MIME_TYPES, "META-INF/nanohttpd/mimetypes.properties"); 167114d6e72cc194ace40662bdf9d81806f14e96d212ritchie if (MIME_TYPES.isEmpty()) { 167214d6e72cc194ace40662bdf9d81806f14e96d212ritchie LOG.log(Level.WARNING, "no mime types found in the classpath! please provide mimetypes.properties"); 16739cd5d3b4438667394ab98895c75b4e1a2f8e76a0Martin M Reed } 16749cd5d3b4438667394ab98895c75b4e1a2f8e76a0Martin M Reed } 167514d6e72cc194ace40662bdf9d81806f14e96d212ritchie return MIME_TYPES; 167614d6e72cc194ace40662bdf9d81806f14e96d212ritchie } 167714d6e72cc194ace40662bdf9d81806f14e96d212ritchie 167814d6e72cc194ace40662bdf9d81806f14e96d212ritchie private static void loadMimeTypes(Map<String, String> result, String resourceName) { 167914d6e72cc194ace40662bdf9d81806f14e96d212ritchie try { 168014d6e72cc194ace40662bdf9d81806f14e96d212ritchie Enumeration<URL> resources = NanoHTTPD.class.getClassLoader().getResources(resourceName); 168114d6e72cc194ace40662bdf9d81806f14e96d212ritchie while (resources.hasMoreElements()) { 168214d6e72cc194ace40662bdf9d81806f14e96d212ritchie URL url = (URL) resources.nextElement(); 168314d6e72cc194ace40662bdf9d81806f14e96d212ritchie Properties properties = new Properties(); 168414d6e72cc194ace40662bdf9d81806f14e96d212ritchie InputStream stream = null; 168514d6e72cc194ace40662bdf9d81806f14e96d212ritchie try { 168614d6e72cc194ace40662bdf9d81806f14e96d212ritchie stream = url.openStream(); 168714d6e72cc194ace40662bdf9d81806f14e96d212ritchie properties.load(url.openStream()); 168814d6e72cc194ace40662bdf9d81806f14e96d212ritchie } catch (IOException e) { 168914d6e72cc194ace40662bdf9d81806f14e96d212ritchie LOG.log(Level.SEVERE, "could not load mimetypes from " + url, e); 169014d6e72cc194ace40662bdf9d81806f14e96d212ritchie } finally { 169114d6e72cc194ace40662bdf9d81806f14e96d212ritchie safeClose(stream); 169214d6e72cc194ace40662bdf9d81806f14e96d212ritchie } 169314d6e72cc194ace40662bdf9d81806f14e96d212ritchie result.putAll((Map) properties); 169414d6e72cc194ace40662bdf9d81806f14e96d212ritchie } 169514d6e72cc194ace40662bdf9d81806f14e96d212ritchie } catch (IOException e) { 169614d6e72cc194ace40662bdf9d81806f14e96d212ritchie LOG.log(Level.INFO, "no mime types available at " + resourceName); 169714d6e72cc194ace40662bdf9d81806f14e96d212ritchie } 169814d6e72cc194ace40662bdf9d81806f14e96d212ritchie }; 169914d6e72cc194ace40662bdf9d81806f14e96d212ritchie 170014d6e72cc194ace40662bdf9d81806f14e96d212ritchie /** 170130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Creates an SSLSocketFactory for HTTPS. Pass a loaded KeyStore and an 170230fb85f55cbd8df3005e652da3781f51294baf90ritchie * array of loaded KeyManagers. These objects must properly 170330fb85f55cbd8df3005e652da3781f51294baf90ritchie * loaded/initialized by the caller. 170430fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 170530fb85f55cbd8df3005e652da3781f51294baf90ritchie public static SSLServerSocketFactory makeSSLSocketFactory(KeyStore loadedKeyStore, KeyManager[] keyManagers) throws IOException { 170630fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLServerSocketFactory res = null; 170730fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 170830fb85f55cbd8df3005e652da3781f51294baf90ritchie TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 170930fb85f55cbd8df3005e652da3781f51294baf90ritchie trustManagerFactory.init(loadedKeyStore); 171030fb85f55cbd8df3005e652da3781f51294baf90ritchie SSLContext ctx = SSLContext.getInstance("TLS"); 171130fb85f55cbd8df3005e652da3781f51294baf90ritchie ctx.init(keyManagers, trustManagerFactory.getTrustManagers(), null); 171230fb85f55cbd8df3005e652da3781f51294baf90ritchie res = ctx.getServerSocketFactory(); 171330fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { 171430fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new IOException(e.getMessage()); 171530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 171630fb85f55cbd8df3005e652da3781f51294baf90ritchie return res; 171730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 171830fb85f55cbd8df3005e652da3781f51294baf90ritchie 171930fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 172030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Creates an SSLSocketFactory for HTTPS. Pass a loaded KeyStore and a 172130fb85f55cbd8df3005e652da3781f51294baf90ritchie * loaded KeyManagerFactory. These objects must properly loaded/initialized 172230fb85f55cbd8df3005e652da3781f51294baf90ritchie * by the caller. 172330fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 172430fb85f55cbd8df3005e652da3781f51294baf90ritchie public static SSLServerSocketFactory makeSSLSocketFactory(KeyStore loadedKeyStore, KeyManagerFactory loadedKeyFactory) throws IOException { 172530fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 17263ea79cfc3ef8447430e6fcc9570c1551831ff9c4ritchie return makeSSLSocketFactory(loadedKeyStore, loadedKeyFactory.getKeyManagers()); 172730fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { 172830fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new IOException(e.getMessage()); 172930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 173030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 173130fb85f55cbd8df3005e652da3781f51294baf90ritchie 173230fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 173330fb85f55cbd8df3005e652da3781f51294baf90ritchie * Creates an SSLSocketFactory for HTTPS. Pass a KeyStore resource with your 173430fb85f55cbd8df3005e652da3781f51294baf90ritchie * certificate and passphrase 173530fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 173630fb85f55cbd8df3005e652da3781f51294baf90ritchie public static SSLServerSocketFactory makeSSLSocketFactory(String keyAndTrustStoreClasspathPath, char[] passphrase) throws IOException { 173730fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 173830fb85f55cbd8df3005e652da3781f51294baf90ritchie KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); 173930fb85f55cbd8df3005e652da3781f51294baf90ritchie InputStream keystoreStream = NanoHTTPD.class.getResourceAsStream(keyAndTrustStoreClasspathPath); 174030fb85f55cbd8df3005e652da3781f51294baf90ritchie keystore.load(keystoreStream, passphrase); 174130fb85f55cbd8df3005e652da3781f51294baf90ritchie KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 174230fb85f55cbd8df3005e652da3781f51294baf90ritchie keyManagerFactory.init(keystore, passphrase); 17433ea79cfc3ef8447430e6fcc9570c1551831ff9c4ritchie return makeSSLSocketFactory(keystore, keyManagerFactory); 174430fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { 174530fb85f55cbd8df3005e652da3781f51294baf90ritchie throw new IOException(e.getMessage()); 174630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 174730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 174830fb85f55cbd8df3005e652da3781f51294baf90ritchie 174914d6e72cc194ace40662bdf9d81806f14e96d212ritchie /** 175014d6e72cc194ace40662bdf9d81806f14e96d212ritchie * Get MIME type from file name extension, if possible 175114d6e72cc194ace40662bdf9d81806f14e96d212ritchie * 175214d6e72cc194ace40662bdf9d81806f14e96d212ritchie * @param uri 175314d6e72cc194ace40662bdf9d81806f14e96d212ritchie * the string representing a file 175414d6e72cc194ace40662bdf9d81806f14e96d212ritchie * @return the connected mime/type 175514d6e72cc194ace40662bdf9d81806f14e96d212ritchie */ 175614d6e72cc194ace40662bdf9d81806f14e96d212ritchie public static String getMimeTypeForFile(String uri) { 175714d6e72cc194ace40662bdf9d81806f14e96d212ritchie int dot = uri.lastIndexOf('.'); 175814d6e72cc194ace40662bdf9d81806f14e96d212ritchie String mime = null; 175914d6e72cc194ace40662bdf9d81806f14e96d212ritchie if (dot >= 0) { 176014d6e72cc194ace40662bdf9d81806f14e96d212ritchie mime = mimeTypes().get(uri.substring(dot + 1).toLowerCase()); 176114d6e72cc194ace40662bdf9d81806f14e96d212ritchie } 176214d6e72cc194ace40662bdf9d81806f14e96d212ritchie return mime == null ? "application/octet-stream" : mime; 176314d6e72cc194ace40662bdf9d81806f14e96d212ritchie } 176414d6e72cc194ace40662bdf9d81806f14e96d212ritchie 176529d1d190b5d5acd569c38ec49794a8698f8ed8c0ritchie private static final void safeClose(Object closeable) { 176629d1d190b5d5acd569c38ec49794a8698f8ed8c0ritchie try { 17676625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen if (closeable != null) { 17686625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen if (closeable instanceof Closeable) { 17696625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen ((Closeable) closeable).close(); 17706625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen } else if (closeable instanceof Socket) { 17716625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen ((Socket) closeable).close(); 17726625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen } else if (closeable instanceof ServerSocket) { 17736625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen ((ServerSocket) closeable).close(); 17746625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen } else { 17756625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen throw new IllegalArgumentException("Unknown object to close"); 17766625f70d53d26ca7b0c039479ed6120d72f2bcb5Jarno Elonen } 177730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 177829d1d190b5d5acd569c38ec49794a8698f8ed8c0ritchie } catch (IOException e) { 177929d1d190b5d5acd569c38ec49794a8698f8ed8c0ritchie NanoHTTPD.LOG.log(Level.SEVERE, "Could not close", e); 178030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 178130fb85f55cbd8df3005e652da3781f51294baf90ritchie } 178230fb85f55cbd8df3005e652da3781f51294baf90ritchie 178330fb85f55cbd8df3005e652da3781f51294baf90ritchie private final String hostname; 178430fb85f55cbd8df3005e652da3781f51294baf90ritchie 178530fb85f55cbd8df3005e652da3781f51294baf90ritchie private final int myPort; 178630fb85f55cbd8df3005e652da3781f51294baf90ritchie 17874344efd565b3ad079eff4780619a964c86ac0b11Jarno Elonen private volatile ServerSocket myServerSocket; 178830fb85f55cbd8df3005e652da3781f51294baf90ritchie 1789529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf private ServerSocketFactory serverSocketFactory = new DefaultServerSocketFactory(); 17908f5edfe4109511a25ccb16f526823435284481adritchie 179130fb85f55cbd8df3005e652da3781f51294baf90ritchie private Thread myThread; 179230fb85f55cbd8df3005e652da3781f51294baf90ritchie 179330fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 179430fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pluggable strategy for asynchronously executing requests. 179530fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 1796d685218eacc23e69f685a76113665f50cc560edfritchie protected AsyncRunner asyncRunner; 179730fb85f55cbd8df3005e652da3781f51294baf90ritchie 179830fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 179930fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pluggable strategy for creating and cleaning up temporary files. 180030fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 180130fb85f55cbd8df3005e652da3781f51294baf90ritchie private TempFileManagerFactory tempFileManagerFactory; 180230fb85f55cbd8df3005e652da3781f51294baf90ritchie 180330fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 180430fb85f55cbd8df3005e652da3781f51294baf90ritchie * Constructs an HTTP server on given port. 180530fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 180630fb85f55cbd8df3005e652da3781f51294baf90ritchie public NanoHTTPD(int port) { 180730fb85f55cbd8df3005e652da3781f51294baf90ritchie this(null, port); 180830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 180930fb85f55cbd8df3005e652da3781f51294baf90ritchie 181030fb85f55cbd8df3005e652da3781f51294baf90ritchie // ------------------------------------------------------------------------------- 181130fb85f55cbd8df3005e652da3781f51294baf90ritchie // // 181230fb85f55cbd8df3005e652da3781f51294baf90ritchie // 181330fb85f55cbd8df3005e652da3781f51294baf90ritchie // Threading Strategy. 181430fb85f55cbd8df3005e652da3781f51294baf90ritchie // 181530fb85f55cbd8df3005e652da3781f51294baf90ritchie // ------------------------------------------------------------------------------- 181630fb85f55cbd8df3005e652da3781f51294baf90ritchie // // 181730fb85f55cbd8df3005e652da3781f51294baf90ritchie 181830fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 181930fb85f55cbd8df3005e652da3781f51294baf90ritchie * Constructs an HTTP server on given hostname and port. 182030fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 182130fb85f55cbd8df3005e652da3781f51294baf90ritchie public NanoHTTPD(String hostname, int port) { 182230fb85f55cbd8df3005e652da3781f51294baf90ritchie this.hostname = hostname; 182330fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myPort = port; 182430fb85f55cbd8df3005e652da3781f51294baf90ritchie setTempFileManagerFactory(new DefaultTempFileManagerFactory()); 182530fb85f55cbd8df3005e652da3781f51294baf90ritchie setAsyncRunner(new DefaultAsyncRunner()); 182630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 182730fb85f55cbd8df3005e652da3781f51294baf90ritchie 182830fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 182930fb85f55cbd8df3005e652da3781f51294baf90ritchie * Forcibly closes all connections that are open. 183030fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 183130fb85f55cbd8df3005e652da3781f51294baf90ritchie public synchronized void closeAllConnections() { 1832abcf1089ce1de49278970f088883cb32acb4f225ritchie stop(); 1833abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1834abcf1089ce1de49278970f088883cb32acb4f225ritchie 1835abcf1089ce1de49278970f088883cb32acb4f225ritchie /** 1836abcf1089ce1de49278970f088883cb32acb4f225ritchie * create a instance of the client handler, subclasses can return a subclass 1837abcf1089ce1de49278970f088883cb32acb4f225ritchie * of the ClientHandler. 18389e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 1839abcf1089ce1de49278970f088883cb32acb4f225ritchie * @param finalAccept 1840abcf1089ce1de49278970f088883cb32acb4f225ritchie * the socket the cleint is connected to 1841abcf1089ce1de49278970f088883cb32acb4f225ritchie * @param inputStream 1842abcf1089ce1de49278970f088883cb32acb4f225ritchie * the input stream 1843abcf1089ce1de49278970f088883cb32acb4f225ritchie * @return the client handler 1844abcf1089ce1de49278970f088883cb32acb4f225ritchie */ 1845abcf1089ce1de49278970f088883cb32acb4f225ritchie protected ClientHandler createClientHandler(final Socket finalAccept, final InputStream inputStream) { 1846abcf1089ce1de49278970f088883cb32acb4f225ritchie return new ClientHandler(inputStream, finalAccept); 1847abcf1089ce1de49278970f088883cb32acb4f225ritchie } 1848abcf1089ce1de49278970f088883cb32acb4f225ritchie 1849abcf1089ce1de49278970f088883cb32acb4f225ritchie /** 1850abcf1089ce1de49278970f088883cb32acb4f225ritchie * Instantiate the server runnable, can be overwritten by subclasses to 1851abcf1089ce1de49278970f088883cb32acb4f225ritchie * provide a subclass of the ServerRunnable. 18529e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 1853abcf1089ce1de49278970f088883cb32acb4f225ritchie * @param timeout 1854abcf1089ce1de49278970f088883cb32acb4f225ritchie * the socet timeout to use. 1855abcf1089ce1de49278970f088883cb32acb4f225ritchie * @return the server runnable. 1856abcf1089ce1de49278970f088883cb32acb4f225ritchie */ 1857abcf1089ce1de49278970f088883cb32acb4f225ritchie protected ServerRunnable createServerRunnable(final int timeout) { 1858abcf1089ce1de49278970f088883cb32acb4f225ritchie return new ServerRunnable(timeout); 185930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 186030fb85f55cbd8df3005e652da3781f51294baf90ritchie 186130fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 186230fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decode parameters from a URL, handing the case where a single parameter 186330fb85f55cbd8df3005e652da3781f51294baf90ritchie * name might have been supplied several times, by return lists of values. 186430fb85f55cbd8df3005e652da3781f51294baf90ritchie * In general these lists will contain a single element. 18659e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 186630fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param parms 186730fb85f55cbd8df3005e652da3781f51294baf90ritchie * original <b>NanoHTTPD</b> parameters values, as passed to the 186830fb85f55cbd8df3005e652da3781f51294baf90ritchie * <code>serve()</code> method. 186930fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return a map of <code>String</code> (parameter name) to 187030fb85f55cbd8df3005e652da3781f51294baf90ritchie * <code>List<String></code> (a list of the values supplied). 187130fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 1872d06acf2b280ff10d396647e3f0f0efe4a65c74afJarno Elonen protected static Map<String, List<String>> decodeParameters(Map<String, String> parms) { 1873d06acf2b280ff10d396647e3f0f0efe4a65c74afJarno Elonen return decodeParameters(parms.get(NanoHTTPD.QUERY_STRING_PARAMETER)); 187430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 187530fb85f55cbd8df3005e652da3781f51294baf90ritchie 1876abcf1089ce1de49278970f088883cb32acb4f225ritchie // ------------------------------------------------------------------------------- 1877abcf1089ce1de49278970f088883cb32acb4f225ritchie // // 1878abcf1089ce1de49278970f088883cb32acb4f225ritchie 187930fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 188030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decode parameters from a URL, handing the case where a single parameter 188130fb85f55cbd8df3005e652da3781f51294baf90ritchie * name might have been supplied several times, by return lists of values. 188230fb85f55cbd8df3005e652da3781f51294baf90ritchie * In general these lists will contain a single element. 18839e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 188430fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param queryString 188530fb85f55cbd8df3005e652da3781f51294baf90ritchie * a query string pulled from the URL. 188630fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return a map of <code>String</code> (parameter name) to 188730fb85f55cbd8df3005e652da3781f51294baf90ritchie * <code>List<String></code> (a list of the values supplied). 188830fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 1889d06acf2b280ff10d396647e3f0f0efe4a65c74afJarno Elonen protected static Map<String, List<String>> decodeParameters(String queryString) { 189030fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, List<String>> parms = new HashMap<String, List<String>>(); 189130fb85f55cbd8df3005e652da3781f51294baf90ritchie if (queryString != null) { 189230fb85f55cbd8df3005e652da3781f51294baf90ritchie StringTokenizer st = new StringTokenizer(queryString, "&"); 189330fb85f55cbd8df3005e652da3781f51294baf90ritchie while (st.hasMoreTokens()) { 189430fb85f55cbd8df3005e652da3781f51294baf90ritchie String e = st.nextToken(); 189530fb85f55cbd8df3005e652da3781f51294baf90ritchie int sep = e.indexOf('='); 189630fb85f55cbd8df3005e652da3781f51294baf90ritchie String propertyName = sep >= 0 ? decodePercent(e.substring(0, sep)).trim() : decodePercent(e).trim(); 189730fb85f55cbd8df3005e652da3781f51294baf90ritchie if (!parms.containsKey(propertyName)) { 189830fb85f55cbd8df3005e652da3781f51294baf90ritchie parms.put(propertyName, new ArrayList<String>()); 189930fb85f55cbd8df3005e652da3781f51294baf90ritchie } 190030fb85f55cbd8df3005e652da3781f51294baf90ritchie String propertyValue = sep >= 0 ? decodePercent(e.substring(sep + 1)) : null; 190130fb85f55cbd8df3005e652da3781f51294baf90ritchie if (propertyValue != null) { 190230fb85f55cbd8df3005e652da3781f51294baf90ritchie parms.get(propertyName).add(propertyValue); 190330fb85f55cbd8df3005e652da3781f51294baf90ritchie } 190430fb85f55cbd8df3005e652da3781f51294baf90ritchie } 190593441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed } 190630fb85f55cbd8df3005e652da3781f51294baf90ritchie return parms; 190730fb85f55cbd8df3005e652da3781f51294baf90ritchie } 190893441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 190930fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 191030fb85f55cbd8df3005e652da3781f51294baf90ritchie * Decode percent encoded <code>String</code> values. 19119e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 191230fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param str 191330fb85f55cbd8df3005e652da3781f51294baf90ritchie * the percent encoded <code>String</code> 191430fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return expanded form of the input, for example "foo%20bar" becomes 191530fb85f55cbd8df3005e652da3781f51294baf90ritchie * "foo bar" 191630fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 1917d06acf2b280ff10d396647e3f0f0efe4a65c74afJarno Elonen protected static String decodePercent(String str) { 191830fb85f55cbd8df3005e652da3781f51294baf90ritchie String decoded = null; 191930fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 192030fb85f55cbd8df3005e652da3781f51294baf90ritchie decoded = URLDecoder.decode(str, "UTF8"); 192130fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (UnsupportedEncodingException ignored) { 192230fb85f55cbd8df3005e652da3781f51294baf90ritchie NanoHTTPD.LOG.log(Level.WARNING, "Encoding not supported, ignored", ignored); 192393441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed } 192430fb85f55cbd8df3005e652da3781f51294baf90ritchie return decoded; 192530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 192693441a0455c683a17622ec2da6768c326e6d7a48Martin M Reed 19278c1502d57d2dddaccc40ab40aa9afa70091704a3ritchie /** 19288c1502d57d2dddaccc40ab40aa9afa70091704a3ritchie * @return true if the gzip compression should be used if the client 1929ddb266b3d98e6430cd892b62525d2b5eee1473b7Jarno Elonen * accespts it. Default this option is on for text content and off 1930d06acf2b280ff10d396647e3f0f0efe4a65c74afJarno Elonen * for everything. Override this for custom semantics. 19318c1502d57d2dddaccc40ab40aa9afa70091704a3ritchie */ 1932ddb266b3d98e6430cd892b62525d2b5eee1473b7Jarno Elonen protected boolean useGzipWhenAccepted(Response r) { 1933ddb266b3d98e6430cd892b62525d2b5eee1473b7Jarno Elonen return r.getMimeType() != null && r.getMimeType().toLowerCase().contains("text/"); 19348c1502d57d2dddaccc40ab40aa9afa70091704a3ritchie } 19358c1502d57d2dddaccc40ab40aa9afa70091704a3ritchie 193630fb85f55cbd8df3005e652da3781f51294baf90ritchie public final int getListeningPort() { 193730fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.myServerSocket == null ? -1 : this.myServerSocket.getLocalPort(); 193830fb85f55cbd8df3005e652da3781f51294baf90ritchie } 1939964393fd7e5ed49088882a126cf82507184467efMartin M Reed 194030fb85f55cbd8df3005e652da3781f51294baf90ritchie public final boolean isAlive() { 194130fb85f55cbd8df3005e652da3781f51294baf90ritchie return wasStarted() && !this.myServerSocket.isClosed() && this.myThread.isAlive(); 19428dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke } 1943964393fd7e5ed49088882a126cf82507184467efMartin M Reed 1944529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf public ServerSocketFactory getServerSocketFactory() { 1945529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf return serverSocketFactory; 1946529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf } 1947529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf 1948529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf public void setServerSocketFactory(ServerSocketFactory serverSocketFactory) { 1949529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf this.serverSocketFactory = serverSocketFactory; 1950529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf } 1951529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf 1952529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf public String getHostname() { 1953529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf return hostname; 1954529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf } 1955529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf 1956529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf public TempFileManagerFactory getTempFileManagerFactory() { 1957529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf return tempFileManagerFactory; 1958529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf } 1959529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf 196030fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 196130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Call before start() to serve over HTTPS instead of HTTP 196230fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 19638f5edfe4109511a25ccb16f526823435284481adritchie public void makeSecure(SSLServerSocketFactory sslServerSocketFactory, String[] sslProtocols) { 1964529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf this.serverSocketFactory = new SecureServerSocketFactory(sslServerSocketFactory, sslProtocols); 196530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 19669058464950a9734da0a7ff2dc47f3081bbb5117critchie 196730fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 19685e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie * Create a response with unknown length (using HTTP 1.1 chunking). 19695e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie */ 1970504cf71fb77ee34a9b37a632664c58f89ab1984eJarno Elonen public static Response newChunkedResponse(IStatus status, String mimeType, InputStream data) { 19715e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return new Response(status, mimeType, data, -1); 19725e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } 19735e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie 19745e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie /** 19755e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie * Create a response with known length. 19765e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie */ 1977504cf71fb77ee34a9b37a632664c58f89ab1984eJarno Elonen public static Response newFixedLengthResponse(IStatus status, String mimeType, InputStream data, long totalBytes) { 19785e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return new Response(status, mimeType, data, totalBytes); 19795e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } 19805e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie 19815e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie /** 19825e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie * Create a text response with known length. 19835e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie */ 1984504cf71fb77ee34a9b37a632664c58f89ab1984eJarno Elonen public static Response newFixedLengthResponse(IStatus status, String mimeType, String txt) { 19855e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie if (txt == null) { 19865e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return newFixedLengthResponse(status, mimeType, new ByteArrayInputStream(new byte[0]), 0); 19875e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } else { 19885e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie byte[] bytes; 19895e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie try { 19905e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie bytes = txt.getBytes("UTF-8"); 19915e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } catch (UnsupportedEncodingException e) { 19925e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie NanoHTTPD.LOG.log(Level.SEVERE, "encoding problem, responding nothing", e); 19935e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie bytes = new byte[0]; 19945e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } 19955e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return newFixedLengthResponse(status, mimeType, new ByteArrayInputStream(bytes), bytes.length); 19965e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } 19975e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } 19985e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie 19995e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie /** 20005e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie * Create a text response with known length. 20015e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie */ 2002504cf71fb77ee34a9b37a632664c58f89ab1984eJarno Elonen public static Response newFixedLengthResponse(String msg) { 20035e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return newFixedLengthResponse(Status.OK, NanoHTTPD.MIME_HTML, msg); 20045e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie } 20055e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie 20065e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie /** 200730fb85f55cbd8df3005e652da3781f51294baf90ritchie * Override this to customize the server. 200830fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 200930fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 201030fb85f55cbd8df3005e652da3781f51294baf90ritchie * (By default, this returns a 404 "Not Found" plain text error response.) 20119e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 201230fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param session 201330fb85f55cbd8df3005e652da3781f51294baf90ritchie * The HTTP session 201430fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return HTTP response, see class Response for details 201530fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 201630fb85f55cbd8df3005e652da3781f51294baf90ritchie public Response serve(IHTTPSession session) { 201730fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> files = new HashMap<String, String>(); 201830fb85f55cbd8df3005e652da3781f51294baf90ritchie Method method = session.getMethod(); 201930fb85f55cbd8df3005e652da3781f51294baf90ritchie if (Method.PUT.equals(method) || Method.POST.equals(method)) { 202030fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 202130fb85f55cbd8df3005e652da3781f51294baf90ritchie session.parseBody(files); 202230fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (IOException ioe) { 20235e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage()); 202430fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (ResponseException re) { 20255e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return newFixedLengthResponse(re.getStatus(), NanoHTTPD.MIME_PLAINTEXT, re.getMessage()); 202630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 20275820f308631a825a05feddddd0aafb962f272b9bPaul Hawke } 20285820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 202930fb85f55cbd8df3005e652da3781f51294baf90ritchie Map<String, String> parms = session.getParms(); 203030fb85f55cbd8df3005e652da3781f51294baf90ritchie parms.put(NanoHTTPD.QUERY_STRING_PARAMETER, session.getQueryParameterString()); 203130fb85f55cbd8df3005e652da3781f51294baf90ritchie return serve(session.getUri(), method, session.getHeaders(), parms, files); 203230fb85f55cbd8df3005e652da3781f51294baf90ritchie } 20335820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 203430fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 203530fb85f55cbd8df3005e652da3781f51294baf90ritchie * Override this to customize the server. 203630fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 203730fb85f55cbd8df3005e652da3781f51294baf90ritchie * <p/> 203830fb85f55cbd8df3005e652da3781f51294baf90ritchie * (By default, this returns a 404 "Not Found" plain text error response.) 20399e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 204030fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param uri 204130fb85f55cbd8df3005e652da3781f51294baf90ritchie * Percent-decoded URI without parameters, for example 204230fb85f55cbd8df3005e652da3781f51294baf90ritchie * "/index.cgi" 204330fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param method 204430fb85f55cbd8df3005e652da3781f51294baf90ritchie * "GET", "POST" etc. 204530fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param parms 204630fb85f55cbd8df3005e652da3781f51294baf90ritchie * Parsed, percent decoded parameters from URI and, in case of 204730fb85f55cbd8df3005e652da3781f51294baf90ritchie * POST, data. 204830fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param headers 204930fb85f55cbd8df3005e652da3781f51294baf90ritchie * Header entries, percent decoded 205030fb85f55cbd8df3005e652da3781f51294baf90ritchie * @return HTTP response, see class Response for details 205130fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 205230fb85f55cbd8df3005e652da3781f51294baf90ritchie @Deprecated 205330fb85f55cbd8df3005e652da3781f51294baf90ritchie public Response serve(String uri, Method method, Map<String, String> headers, Map<String, String> parms, Map<String, String> files) { 20545e2e2f19e06b9f4a01c6da83a2297eb18fe2b546ritchie return newFixedLengthResponse(Response.Status.NOT_FOUND, NanoHTTPD.MIME_PLAINTEXT, "Not Found"); 205530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 20565820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 205730fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 205830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pluggable strategy for asynchronously executing requests. 20599e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 206030fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param asyncRunner 206130fb85f55cbd8df3005e652da3781f51294baf90ritchie * new strategy for handling threads. 206230fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 206330fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setAsyncRunner(AsyncRunner asyncRunner) { 206430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.asyncRunner = asyncRunner; 206530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 20665820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 206730fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 206830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Pluggable strategy for creating and cleaning up temporary files. 20699e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 207030fb85f55cbd8df3005e652da3781f51294baf90ritchie * @param tempFileManagerFactory 207130fb85f55cbd8df3005e652da3781f51294baf90ritchie * new strategy for handling temp files. 207230fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 207330fb85f55cbd8df3005e652da3781f51294baf90ritchie public void setTempFileManagerFactory(TempFileManagerFactory tempFileManagerFactory) { 207430fb85f55cbd8df3005e652da3781f51294baf90ritchie this.tempFileManagerFactory = tempFileManagerFactory; 20755820f308631a825a05feddddd0aafb962f272b9bPaul Hawke } 20765820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 20778dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke /** 207830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Start the server. 20799e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 208030fb85f55cbd8df3005e652da3781f51294baf90ritchie * @throws IOException 208130fb85f55cbd8df3005e652da3781f51294baf90ritchie * if the socket is in use. 20828dd183554e56783ecf7cd6b8529cf807b9eb813cPaul Hawke */ 208330fb85f55cbd8df3005e652da3781f51294baf90ritchie public void start() throws IOException { 2084c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie start(NanoHTTPD.SOCKET_READ_TIMEOUT); 2085c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie } 2086c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie 2087c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie /** 2088529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf * Starts the server (in setDaemon(true) mode). 2089529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf */ 2090529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf public void start(final int timeout) throws IOException { 2091529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf start(timeout, true); 2092529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf } 2093529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf 2094529d17d550fc2928f69c504967e644460e5c9d73hoelzlwimmerf /** 2095c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie * Start the server. 20969e1ec7bff40d70d31953a04dd448665aaf549395ritchie * 2097c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie * @param timeout 2098c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie * timeout to use for socket connections. 20999e659197ebe52fa61801c20b913d666087f98eb4Jarno Elonen * @param daemon 21009e659197ebe52fa61801c20b913d666087f98eb4Jarno Elonen * start the thread daemon or not. 2101c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie * @throws IOException 2102c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie * if the socket is in use. 2103c88ba6d829b5c4430a5851c1c953e94ea93a692fritchie */ 21049e659197ebe52fa61801c20b913d666087f98eb4Jarno Elonen public void start(final int timeout, boolean daemon) throws IOException { 2105e316510734c3e986f89155e0a3916ae6c5167395hoelzlwimmerf this.myServerSocket = this.getServerSocketFactory().create(); 210630fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myServerSocket.setReuseAddress(true); 21079058464950a9734da0a7ff2dc47f3081bbb5117critchie 2108284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie ServerRunnable serverRunnable = createServerRunnable(timeout); 2109284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie this.myThread = new Thread(serverRunnable); 21109e659197ebe52fa61801c20b913d666087f98eb4Jarno Elonen this.myThread.setDaemon(daemon); 211130fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myThread.setName("NanoHttpd Main Listener"); 211230fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myThread.start(); 2113284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie while (!serverRunnable.hasBinded && serverRunnable.bindException == null) { 2114284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie try { 2115284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie Thread.sleep(10L); 2116284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie } catch (Throwable e) { 2117284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie // on android this may not be allowed, that's why we 2118284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie // catch throwable the wait should be very short because we are 2119284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie // just waiting for the bind of the socket 2120284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie } 2121284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie } 2122284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie if (serverRunnable.bindException != null) { 2123284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie throw serverRunnable.bindException; 2124284d64856c4ab6629228cfdb5437e54d0bd0bf7aritchie } 212530fb85f55cbd8df3005e652da3781f51294baf90ritchie } 21265820f308631a825a05feddddd0aafb962f272b9bPaul Hawke 212730fb85f55cbd8df3005e652da3781f51294baf90ritchie /** 212830fb85f55cbd8df3005e652da3781f51294baf90ritchie * Stop the server. 212930fb85f55cbd8df3005e652da3781f51294baf90ritchie */ 213030fb85f55cbd8df3005e652da3781f51294baf90ritchie public void stop() { 213130fb85f55cbd8df3005e652da3781f51294baf90ritchie try { 213230fb85f55cbd8df3005e652da3781f51294baf90ritchie safeClose(this.myServerSocket); 2133abcf1089ce1de49278970f088883cb32acb4f225ritchie this.asyncRunner.closeAll(); 213430fb85f55cbd8df3005e652da3781f51294baf90ritchie if (this.myThread != null) { 213530fb85f55cbd8df3005e652da3781f51294baf90ritchie this.myThread.join(); 213630fb85f55cbd8df3005e652da3781f51294baf90ritchie } 213730fb85f55cbd8df3005e652da3781f51294baf90ritchie } catch (Exception e) { 213830fb85f55cbd8df3005e652da3781f51294baf90ritchie NanoHTTPD.LOG.log(Level.SEVERE, "Could not stop all connections", e); 2139964393fd7e5ed49088882a126cf82507184467efMartin M Reed } 214030fb85f55cbd8df3005e652da3781f51294baf90ritchie } 2141964393fd7e5ed49088882a126cf82507184467efMartin M Reed 214230fb85f55cbd8df3005e652da3781f51294baf90ritchie public final boolean wasStarted() { 214330fb85f55cbd8df3005e652da3781f51294baf90ritchie return this.myServerSocket != null && this.myThread != null; 21447e423dde0fe863fadbd363de8a94746a30fca0eeMartin M Reed } 2145269baff79590de5abd70e8e05bf46547e4a28ee6Micah Hainline and Paul Hawke} 2146