Android作为HTTP服务器–NanoHTTPD源码分析

NanoHTTPD源码以及解释

 

[java]
  1. package com.jwzhangjie.shafa.market.remotesetup;
  2. import java.io.BufferedReader;
  3. import java.io.ByteArrayInputStream;
  4. import java.io.Closeable;
  5. import java.io.File;
  6. import java.io.FileInputStream;
  7. import java.io.FileOutputStream;
  8. import java.io.IOException;
  9. import java.io.InputStream;
  10. import java.io.InputStreamReader;
  11. import java.io.OutputStream;
  12. import java.io.PrintWriter;
  13. import java.io.RandomAccessFile;
  14. import java.io.PushbackInputStream;
  15. import java.io.UnsupportedEncodingException;
  16. import java.net.InetAddress;
  17. import java.net.InetSocketAddress;
  18. import java.net.ServerSocket;
  19. import java.net.Socket;
  20. import java.net.SocketException;
  21. import java.net.SocketTimeoutException;
  22. import java.net.URLDecoder;
  23. import java.nio.ByteBuffer;
  24. import java.nio.channels.FileChannel;
  25. import java.text.SimpleDateFormat;
  26. import java.util.ArrayList;
  27. import java.util.Calendar;
  28. import java.util.Date;
  29. import java.util.HashMap;
  30. import java.util.HashSet;
  31. import java.util.Iterator;
  32. import java.util.List;
  33. import java.util.Locale;
  34. import java.util.Map;
  35. import java.util.Set;
  36. import java.util.StringTokenizer;
  37. import java.util.TimeZone;
  38. /**
  39.  * A simple, tiny, nicely embeddable HTTP server in Java
  40.  * <p/>
  41.  * <p/>
  42.  * NanoHTTPD
  43.  * <p></p>Copyright (c) 2012-2013 by Paul S. Hawke, 2001,2005-2013 by Jarno Elonen, 2010 by Konstantinos Togias</p>
  44.  * <p/>
  45.  * <p/>
  46.  * <b>Features + limitations: </b>
  47.  * <ul>
  48.  * <p/>
  49.  * <li>Only one Java file</li>
  50.  * <li>Java 5 compatible</li>
  51.  * <li>Released as open source, Modified BSD licence</li>
  52.  * <li>No fixed config files, logging, authorization etc. (Implement yourself if you need them.)</li>
  53.  * <li>Supports parameter parsing of GET and POST methods (+ rudimentary PUT support in 1.25)</li>
  54.  * <li>Supports both dynamic content and file serving</li>
  55.  * <li>Supports file upload (since version 1.2, 2010)</li>
  56.  * <li>Supports partial content (streaming)</li>
  57.  * <li>Supports ETags</li>
  58.  * <li>Never caches anything</li>
  59.  * <li>Doesn’t limit bandwidth, request time or simultaneous connections</li>
  60.  * <li>Default code serves files and shows all HTTP parameters and headers</li>
  61.  * <li>File server supports directory listing, index.html and index.htm</li>
  62.  * <li>File server supports partial content (streaming)</li>
  63.  * <li>File server supports ETags</li>
  64.  * <li>File server does the 301 redirection trick for directories without ‘/'</li>
  65.  * <li>File server supports simple skipping for files (continue download)</li>
  66.  * <li>File server serves also very long files without memory overhead</li>
  67.  * <li>Contains a built-in list of most common mime types</li>
  68.  * <li>All header names are converted lowercase so they don’t vary between browsers/clients</li>
  69.  * <p/>
  70.  * </ul>
  71.  * <p/>
  72.  * <p/>
  73.  * <b>How to use: </b>
  74.  * <ul>
  75.  * <p/>
  76.  * <li>Subclass and implement serve() and embed to your own program</li>
  77.  * <p/>
  78.  * </ul>
  79.  * <p/>
  80.  * See the separate “LICENSE.md” file for the distribution license (Modified BSD licence)
  81.  */
  82. public abstract class NanoHTTPD {
  83.     /**
  84.      * Maximum time to wait on Socket.getInputStream().read() (in milliseconds)
  85.      * This is required as the Keep-Alive HTTP connections would otherwise
  86.      * block the socket reading thread forever (or as long the browser is open).
  87.      */
  88.     public static final int SOCKET_READ_TIMEOUT = 5000;
  89.     /**
  90.      * Common mime type for dynamic content: plain text
  91.      */
  92.     public static final String MIME_PLAINTEXT = “text/plain”;
  93.     /**
  94.      * Common mime type for dynamic content: html
  95.      */
  96.     public static final String MIME_HTML = “text/html”;
  97.     /**
  98.      * Pseudo-Parameter to use to store the actual query string in the parameters map for later re-processing.
  99.      */
  100.     private static final String QUERY_STRING_PARAMETER = “NanoHttpd.QUERY_STRING”;
  101.     private final String hostname;
  102.     private final int myPort;
  103.     private ServerSocket myServerSocket;
  104.     private Set<Socket> openConnections = new HashSet<Socket>();
  105.     private Thread myThread;
  106.     /**
  107.      * Pluggable strategy for asynchronously executing requests.
  108.      */
  109.     private AsyncRunner asyncRunner;
  110.     /**
  111.      * Pluggable strategy for creating and cleaning up temporary files.
  112.      */
  113.     private TempFileManagerFactory tempFileManagerFactory;
  114.     /**
  115.      * Constructs an HTTP server on given port.
  116.      */
  117.     public NanoHTTPD(int port) {
  118.         this(null, port);
  119.     }
  120.     /**
  121.      * Constructs an HTTP server on given hostname and port.
  122.      */
  123.     public NanoHTTPD(String hostname, int port) {
  124.         this.hostname = hostname;
  125.         this.myPort = port;
  126.         setTempFileManagerFactory(new DefaultTempFileManagerFactory());
  127.         setAsyncRunner(new DefaultAsyncRunner());
  128.     }
  129.     private static final void safeClose(Closeable closeable) {
  130.         if (closeable != null) {
  131.             try {
  132.                 closeable.close();
  133.             } catch (IOException e) {
  134.             }
  135.         }
  136.     }
  137.     private static final void safeClose(Socket closeable) {
  138.         if (closeable != null) {
  139.             try {
  140.                 closeable.close();
  141.             } catch (IOException e) {
  142.             }
  143.         }
  144.     }
  145.     private static final void safeClose(ServerSocket closeable) {
  146.         if (closeable != null) {
  147.             try {
  148.                 closeable.close();
  149.             } catch (IOException e) {
  150.             }
  151.         }
  152.     }
  153.     /**
  154.      * Start the server.
  155.      *
  156.      * @throws IOException if the socket is in use.
  157.      */
  158.     public void start() throws IOException {
  159.         myServerSocket = new ServerSocket();
  160.         myServerSocket.bind((hostname != null) ? new InetSocketAddress(hostname, myPort) : new InetSocketAddress(myPort));
  161.         myThread = new Thread(new Runnable() {
  162.             @Override
  163.             public void run() {
  164.                 do {
  165.                     try {
  166.                         final Socket finalAccept = myServerSocket.accept();
  167.                         registerConnection(finalAccept);
  168.                         finalAccept.setSoTimeout(SOCKET_READ_TIMEOUT);
  169.                         final InputStream inputStream = finalAccept.getInputStream();
  170.                         asyncRunner.exec(new Runnable() {
  171.                             @Override
  172.                             public void run() {
  173.                                 OutputStream outputStream = null;
  174.                                 try {
  175.                                     outputStream = finalAccept.getOutputStream();
  176.                                     TempFileManager tempFileManager = tempFileManagerFactory.create();
  177.                                     HTTPSession session = new HTTPSession(tempFileManager, inputStream, outputStream, finalAccept.getInetAddress());
  178.                                     while (!finalAccept.isClosed()) {
  179.                                         session.execute();
  180.                                     }
  181.                                 } catch (Exception e) {
  182.                                     // When the socket is closed by the client, we throw our own SocketException
  183.                                     // to break the  “keep alive” loop above.
  184.                                     if (!(e instanceof SocketException && “NanoHttpd Shutdown”.equals(e.getMessage()))) {
  185.                                         e.printStackTrace();
  186.                                     }
  187.                                 } finally {
  188.                                     safeClose(outputStream);
  189.                                     safeClose(inputStream);
  190.                                     safeClose(finalAccept);
  191.                                     unRegisterConnection(finalAccept);
  192.                                 }
  193.                             }
  194.                         });
  195.                     } catch (IOException e) {
  196.                     }
  197.                 } while (!myServerSocket.isClosed());
  198.             }
  199.         });
  200.         myThread.setDaemon(true);
  201.         myThread.setName(“NanoHttpd Main Listener”);
  202.         myThread.start();
  203.     }
  204.     /**
  205.      * Stop the server.
  206.      */
  207.     public void stop() {
  208.         try {
  209.             safeClose(myServerSocket);
  210.             closeAllConnections();
  211.             myThread.join();
  212.         } catch (Exception e) {
  213.             e.printStackTrace();
  214.         }
  215.     }
  216.     /**
  217.      * Registers that a new connection has been set up.
  218.      *
  219.      * @param socket
  220.      *            the {@link Socket} for the connection.
  221.      */
  222.     public synchronized void registerConnection(Socket socket) {
  223.         openConnections.add(socket);
  224.     }
  225.     /**
  226.      * Registers that a connection has been closed
  227.      *
  228.      * @param socket
  229.      *            the {@link Socket} for the connection.
  230.      */
  231.     public synchronized void unRegisterConnection(Socket socket) {
  232.         openConnections.remove(socket);
  233.     }
  234.     /**
  235.      * Forcibly closes all connections that are open.
  236.      */
  237.     public synchronized void closeAllConnections() {
  238.         for (Socket socket : openConnections) {
  239.             safeClose(socket);
  240.         }
  241.     }
  242.     public final int getListeningPort() {
  243.         return myServerSocket == null ? -1 : myServerSocket.getLocalPort();
  244.     }
  245.     public final boolean wasStarted() {
  246.         return myServerSocket != null && myThread != null;
  247.     }
  248.     public final boolean isAlive() {
  249.         return wasStarted() && !myServerSocket.isClosed() && myThread.isAlive();
  250.     }
  251.     /**
  252.      * Override this to customize the server.
  253.      * <p/>
  254.      * <p/>
  255.      * (By default, this delegates to serveFile() and allows directory listing.)
  256.      *
  257.      * @param uri     Percent-decoded URI without parameters, for example “/index.cgi”
  258.      * @param method  “GET”, “POST” etc.
  259.      * @param parms   Parsed, percent decoded parameters from URI and, in case of POST, data.
  260.      * @param headers Header entries, percent decoded
  261.      * @return HTTP response, see class Response for details
  262.      */
  263.     @Deprecated
  264.     public Response serve(String uri, Method method, Map<String, String> headers, Map<String, String> parms,
  265.                                    Map<String, String> files) {
  266.         return new Response(Response.Status.NOT_FOUND, MIME_PLAINTEXT, “Not Found”);
  267.     }
  268.     /**
  269.      * Override this to customize the server.
  270.      * <p/>
  271.      * <p/>
  272.      * (By default, this delegates to serveFile() and allows directory listing.)
  273.      *
  274.      * @param session The HTTP session
  275.      * @return HTTP response, see class Response for details
  276.      * 我们需要自己实现这个方法
  277.      */
  278.     public Response serve(IHTTPSession session) {
  279.         Map<String, String> files = new HashMap<String, String>();
  280.         Method method = session.getMethod();
  281.         if (Method.PUT.equals(method) || Method.POST.equals(method)) {
  282.             try {
  283.                 session.parseBody(files);
  284.             } catch (IOException ioe) {
  285.                 return new Response(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, “SERVER INTERNAL ERROR: IOException: ” + ioe.getMessage());
  286.             } catch (ResponseException re) {
  287.                 return new Response(re.getStatus(), MIME_PLAINTEXT, re.getMessage());
  288.             }
  289.         }
  290.         Map<String, String> parms = session.getParms();
  291.         parms.put(QUERY_STRING_PARAMETER, session.getQueryParameterString());
  292.         return serve(session.getUri(), method, session.getHeaders(), parms, files);
  293.     }
  294.     /**
  295.      * Decode percent encoded <code>String</code> values.
  296.      *
  297.      * @param str the percent encoded <code>String</code>
  298.      * @return expanded form of the input, for example “foo%20bar” becomes “foo bar”
  299.      */
  300.     protected String decodePercent(String str) {
  301.         String decoded = null;
  302.         try {
  303.             decoded = URLDecoder.decode(str, “UTF8”);
  304.         } catch (UnsupportedEncodingException ignored) {
  305.         }
  306.         return decoded;
  307.     }
  308.     /**
  309.      * Decode parameters from a URL, handing the case where a single parameter name might have been
  310.      * supplied several times, by return lists of values.  In general these lists will contain a single
  311.      * element.
  312.      *
  313.      * @param parms original <b>NanoHttpd</b> parameters values, as passed to the <code>serve()</code> method.
  314.      * @return a map of <code>String</code> (parameter name) to <code>List<String></code> (a list of the values supplied).
  315.      */
  316.     protected Map<String, List<String>> decodeParameters(Map<String, String> parms) {
  317.         return this.decodeParameters(parms.get(QUERY_STRING_PARAMETER));
  318.     }
  319.     /**
  320.      * Decode parameters from a URL, handing the case where a single parameter name might have been
  321.      * supplied several times, by return lists of values.  In general these lists will contain a single
  322.      * element.
  323.      *
  324.      * @param queryString a query string pulled from the URL.
  325.      * @return a map of <code>String</code> (parameter name) to <code>List<String></code> (a list of the values supplied).
  326.      */
  327.     protected Map<String, List<String>> decodeParameters(String queryString) {
  328.         Map<String, List<String>> parms = new HashMap<String, List<String>>();
  329.         if (queryString != null) {
  330.             StringTokenizer st = new StringTokenizer(queryString, “&”);
  331.             while (st.hasMoreTokens()) {
  332.                 String e = st.nextToken();
  333.                 int sep = e.indexOf(‘=’);
  334.                 String propertyName = (sep >= 0) ? decodePercent(e.substring(0, sep)).trim() : decodePercent(e).trim();
  335.                 if (!parms.containsKey(propertyName)) {
  336.                     parms.put(propertyName, new ArrayList<String>());
  337.                 }
  338.                 String propertyValue = (sep >= 0) ? decodePercent(e.substring(sep + 1)) : null;
  339.                 if (propertyValue != null) {
  340.                     parms.get(propertyName).add(propertyValue);
  341.                 }
  342.             }
  343.         }
  344.         return parms;
  345.     }
  346.     // ——————————————————————————- //
  347.     //
  348.     // Threading Strategy.
  349.     //
  350.     // ——————————————————————————- //
  351.     /**
  352.      * Pluggable strategy for asynchronously executing requests.
  353.      *
  354.      * @param asyncRunner new strategy for handling threads.
  355.      */
  356.     public void setAsyncRunner(AsyncRunner asyncRunner) {
  357.         this.asyncRunner = asyncRunner;
  358.     }
  359.     // ——————————————————————————- //
  360.     //
  361.     // Temp file handling strategy.
  362.     //
  363.     // ——————————————————————————- //
  364.     /**
  365.      * Pluggable strategy for creating and cleaning up temporary files.
  366.      *
  367.      * @param tempFileManagerFactory new strategy for handling temp files.
  368.      */
  369.     public void setTempFileManagerFactory(TempFileManagerFactory tempFileManagerFactory) {
  370.         this.tempFileManagerFactory = tempFileManagerFactory;
  371.     }
  372.     /**
  373.      * HTTP Request methods, with the ability to decode a <code>String</code> back to its enum value.
  374.      */
  375.     public enum Method {
  376.         GET, PUT, POST, DELETE, HEAD, OPTIONS;
  377.         static Method lookup(String method) {
  378.             for (Method m : Method.values()) {
  379.                 if (m.toString().equalsIgnoreCase(method)) {
  380.                     return m;
  381.                 }
  382.             }
  383.             return null;
  384.         }
  385.     }
  386.     /**
  387.      * Pluggable strategy for asynchronously executing requests.
  388.      */
  389.     public interface AsyncRunner {
  390.         void exec(Runnable code);
  391.     }
  392.     /**
  393.      * Factory to create temp file managers.
  394.      */
  395.     public interface TempFileManagerFactory {
  396.         TempFileManager create();
  397.     }
  398.     // ——————————————————————————- //
  399.     /**
  400.      * Temp file manager.
  401.      * <p/>
  402.      * <p>Temp file managers are created 1-to-1 with incoming requests, to create and cleanup
  403.      * temporary files created as a result of handling the request.</p>
  404.      */
  405.     public interface TempFileManager {
  406.         TempFile createTempFile() throws Exception;
  407.         void clear();
  408.     }
  409.     /**
  410.      * A temp file.
  411.      * <p/>
  412.      * <p>Temp files are responsible for managing the actual temporary storage and cleaning
  413.      * themselves up when no longer needed.</p>
  414.      */
  415.     public interface TempFile {
  416.         OutputStream open() throws Exception;
  417.         void delete() throws Exception;
  418.         String getName();
  419.     }
  420.     /**
  421.      * Default threading strategy for NanoHttpd.
  422.      * <p/>
  423.      * <p>By default, the server spawns a new Thread for every incoming request.  These are set
  424.      * to <i>daemon</i> status, and named according to the request number.  The name is
  425.      * useful when profiling the application.</p>
  426.      */
  427.     public static class DefaultAsyncRunner implements AsyncRunner {
  428.         private long requestCount;
  429.         @Override
  430.         public void exec(Runnable code) {
  431.             ++requestCount;
  432.             Thread t = new Thread(code);
  433.             t.setDaemon(true);
  434.             t.setName(“NanoHttpd Request Processor (#” + requestCount + “)”);
  435.             t.start();
  436.         }
  437.     }
  438.     /**
  439.      * Default strategy for creating and cleaning up temporary files.
  440.      * <p/>
  441.      * <p></p>This class stores its files in the standard location (that is,
  442.      * wherever <code>java.io.tmpdir</code> points to).  Files are added
  443.      * to an internal list, and deleted when no longer needed (that is,
  444.      * when <code>clear()</code> is invoked at the end of processing a
  445.      * request).</p>
  446.      */
  447.     public static class DefaultTempFileManager implements TempFileManager {
  448.         private final String tmpdir;
  449.         private final List<TempFile> tempFiles;
  450.         public DefaultTempFileManager() {
  451.             tmpdir = System.getProperty(“java.io.tmpdir”);
  452.             tempFiles = new ArrayList<TempFile>();
  453.         }
  454.         @Override
  455.         public TempFile createTempFile() throws Exception {
  456.             DefaultTempFile tempFile = new DefaultTempFile(tmpdir);
  457.             tempFiles.add(tempFile);
  458.             return tempFile;
  459.         }
  460.         @Override
  461.         public void clear() {
  462.             for (TempFile file : tempFiles) {
  463.                 try {
  464.                     file.delete();
  465.                 } catch (Exception ignored) {
  466.                 }
  467.             }
  468.             tempFiles.clear();
  469.         }
  470.     }
  471.     /**
  472.      * Default strategy for creating and cleaning up temporary files.
  473.      * <p/>
  474.      * <p></p></[>By default, files are created by <code>File.createTempFile()</code> in
  475.      * the directory specified.</p>
  476.      */
  477.     public static class DefaultTempFile implements TempFile {
  478.         private File file;
  479.         private OutputStream fstream;
  480.         public DefaultTempFile(String tempdir) throws IOException {
  481.             file = File.createTempFile(“NanoHTTPD-“, “”, new File(tempdir));
  482.             fstream = new FileOutputStream(file);
  483.         }
  484.         @Override
  485.         public OutputStream open() throws Exception {
  486.             return fstream;
  487.         }
  488.         @Override
  489.         public void delete() throws Exception {
  490.             safeClose(fstream);
  491.             file.delete();
  492.         }
  493.         @Override
  494.         public String getName() {
  495.             return file.getAbsolutePath();
  496.         }
  497.     }
  498.     /**
  499.      * HTTP response. Return one of these from serve().
  500.      */
  501.     public static class Response {
  502.         /**
  503.          * HTTP status code after processing, e.g. “200 OK”, HTTP_OK
  504.          */
  505.         private Status status;
  506.         /**
  507.          * MIME type of content, e.g. “text/html”
  508.          */
  509.         private String mimeType;
  510.         /**
  511.          * Data of the response, may be null.
  512.          */
  513.         private InputStream data;
  514.         /**
  515.          * Headers for the HTTP response. Use addHeader() to add lines.
  516.          */
  517.         private Map<String, String> header = new HashMap<String, String>();
  518.         /**
  519.          * The request method that spawned this response.
  520.          */
  521.         private Method requestMethod;
  522.         /**
  523.          * Use chunkedTransfer
  524.          */
  525.         private boolean chunkedTransfer;
  526.         /**
  527.          * Default constructor: response = HTTP_OK, mime = MIME_HTML and your supplied message
  528.          */
  529.         public Response(String msg) {
  530.             this(Status.OK, MIME_HTML, msg);
  531.         }
  532.         /**
  533.          * Basic constructor.
  534.          */
  535.         public Response(Status status, String mimeType, InputStream data) {
  536.             this.status = status;
  537.             this.mimeType = mimeType;
  538.             this.data = data;
  539.         }
  540.         /**
  541.          * Convenience method that makes an InputStream out of given text.
  542.          */
  543.         public Response(Status status, String mimeType, String txt) {
  544.             this.status = status;
  545.             this.mimeType = mimeType;
  546.             try {
  547.                 this.data = txt != null ? new ByteArrayInputStream(txt.getBytes(“UTF-8”)) : null;
  548.             } catch (java.io.UnsupportedEncodingException uee) {
  549.                 uee.printStackTrace();
  550.             }
  551.         }
  552.         /**
  553.          * Adds given line to the header.
  554.          */
  555.         public void addHeader(String name, String value) {
  556.             header.put(name, value);
  557.         }
  558.         /**
  559.          * Sends given response to the socket.
  560.          */
  561.         private void send(OutputStream outputStream) {
  562.             String mime = mimeType;
  563.             SimpleDateFormat gmtFrmt = new SimpleDateFormat(“E, d MMM yyyy HH:mm:ss ‘GMT'”, Locale.US);
  564.             gmtFrmt.setTimeZone(TimeZone.getTimeZone(“GMT”));
  565.             try {
  566.                 if (status == null) {
  567.                     throw new Error(“sendResponse(): Status can’t be null.”);
  568.                 }
  569.                 PrintWriter pw = new PrintWriter(outputStream);
  570.                 pw.print(“HTTP/1.1 ” + status.getDescription() + ” \r\n”);
  571.                 if (mime != null) {
  572.                     pw.print(“Content-Type: ” + mime + “\r\n”);
  573.                 }
  574.                 if (header == null || header.get(“Date”) == null) {
  575.                     pw.print(“Date: ” + gmtFrmt.format(new Date()) + “\r\n”);
  576.                 }
  577.                 if (header != null) {
  578.                     for (String key : header.keySet()) {
  579.                         String value = header.get(key);
  580.                         pw.print(key + “: ” + value + “\r\n”);
  581.                     }
  582.                 }
  583.                 pw.print(“Connection: keep-alive\r\n”);
  584.                 if (requestMethod != Method.HEAD && chunkedTransfer) {
  585.                     sendAsChunked(outputStream, pw);
  586.                 } else {
  587.                     sendAsFixedLength(outputStream, pw);
  588.                 }
  589.                 outputStream.flush();
  590.                 safeClose(data);
  591.             } catch (IOException ioe) {
  592.                 // Couldn’t write? No can do.
  593.             }
  594.         }
  595.         private void sendAsChunked(OutputStream outputStream, PrintWriter pw) throws IOException {
  596.             pw.print(“Transfer-Encoding: chunked\r\n”);
  597.             pw.print(“\r\n”);
  598.             pw.flush();
  599.             int BUFFER_SIZE = 16 * 1024;
  600.             byte[] CRLF = “\r\n”.getBytes();
  601.             byte[] buff = new byte[BUFFER_SIZE];
  602.             int read;
  603.             while ((read = data.read(buff)) > 0) {
  604.                 outputStream.write(String.format(“%x\r\n”, read).getBytes());
  605.                 outputStream.write(buff, 0, read);
  606.                 outputStream.write(CRLF);
  607.             }
  608.             outputStream.write(String.format(“0\r\n\r\n”).getBytes());
  609.         }
  610.         private void sendAsFixedLength(OutputStream outputStream, PrintWriter pw) throws IOException {
  611.             int pending = data != null ? data.available() : 0; // This is to support partial sends, see serveFile()
  612.             pw.print(“Content-Length: “+pending+”\r\n”);
  613.             pw.print(“\r\n”);
  614.             pw.flush();
  615.             if (requestMethod != Method.HEAD && data != null) {
  616.                 int BUFFER_SIZE = 16 * 1024;
  617.                 byte[] buff = new byte[BUFFER_SIZE];
  618.                 while (pending > 0) {
  619.                     int read = data.read(buff, 0, ((pending > BUFFER_SIZE) ? BUFFER_SIZE : pending));
  620.                     if (read <= 0) {
  621.                         break;
  622.                     }
  623.                     outputStream.write(buff, 0, read);
  624.                     pending -= read;
  625.                 }
  626.             }
  627.         }
  628.         public Status getStatus() {
  629.             return status;
  630.         }
  631.         public void setStatus(Status status) {
  632.             this.status = status;
  633.         }
  634.         public String getMimeType() {
  635.             return mimeType;
  636.         }
  637.         public void setMimeType(String mimeType) {
  638.             this.mimeType = mimeType;
  639.         }
  640.         public InputStream getData() {
  641.             return data;
  642.         }
  643.         public void setData(InputStream data) {
  644.             this.data = data;
  645.         }
  646.         public Method getRequestMethod() {
  647.             return requestMethod;
  648.         }
  649.         public void setRequestMethod(Method requestMethod) {
  650.             this.requestMethod = requestMethod;
  651.         }
  652.         public void setChunkedTransfer(boolean chunkedTransfer) {
  653.             this.chunkedTransfer = chunkedTransfer;
  654.         }
  655.         /**
  656.          * Some HTTP response status codes
  657.          */
  658.         public enum Status {
  659.             OK(200, “OK”), CREATED(201, “Created”), ACCEPTED(202, “Accepted”), NO_CONTENT(204, “No Content”), PARTIAL_CONTENT(206, “Partial Content”), REDIRECT(301,
  660.                 “Moved Permanently”), NOT_MODIFIED(304, “Not Modified”), BAD_REQUEST(400, “Bad Request”), UNAUTHORIZED(401,
  661.                 “Unauthorized”), FORBIDDEN(403, “Forbidden”), NOT_FOUND(404, “Not Found”), METHOD_NOT_ALLOWED(405, “Method Not Allowed”), RANGE_NOT_SATISFIABLE(416,
  662.                 “Requested Range Not Satisfiable”), INTERNAL_ERROR(500, “Internal Server Error”);
  663.             private final int requestStatus;
  664.             private final String description;
  665.             Status(int requestStatus, String description) {
  666.                 this.requestStatus = requestStatus;
  667.                 this.description = description;
  668.             }
  669.             public int getRequestStatus() {
  670.                 return this.requestStatus;
  671.             }
  672.             public String getDescription() {
  673.                 return “” + this.requestStatus + ” ” + description;
  674.             }
  675.         }
  676.     }
  677.     public static final class ResponseException extends Exception {
  678.         private final Response.Status status;
  679.         public ResponseException(Response.Status status, String message) {
  680.             super(message);
  681.             this.status = status;
  682.         }
  683.         public ResponseException(Response.Status status, String message, Exception e) {
  684.             super(message, e);
  685.             this.status = status;
  686.         }
  687.         public Response.Status getStatus() {
  688.             return status;
  689.         }
  690.     }
  691.     /**
  692.      * Default strategy for creating and cleaning up temporary files.
  693.      */
  694.     private class DefaultTempFileManagerFactory implements TempFileManagerFactory {
  695.         @Override
  696.         public TempFileManager create() {
  697.             return new DefaultTempFileManager();
  698.         }
  699.     }
  700.     /**
  701.      * Handles one session, i.e. parses the HTTP request and returns the response.
  702.      */
  703.     public interface IHTTPSession {
  704.         void execute() throws IOException;
  705.         Map<String, String> getParms();
  706.         Map<String, String> getHeaders();
  707.         /**
  708.          * @return the path part of the URL.
  709.          */
  710.         String getUri();
  711.         String getQueryParameterString();
  712.         Method getMethod();
  713.         InputStream getInputStream();
  714.         CookieHandler getCookies();
  715.         /**
  716.          * Adds the files in the request body to the files map.
  717.          * @arg files – map to modify
  718.          */
  719.         void parseBody(Map<String, String> files) throws IOException, ResponseException;
  720.     }
  721.     protected class HTTPSession implements IHTTPSession {
  722.         public static final int BUFSIZE = 8192;
  723.         private final TempFileManager tempFileManager;
  724.         private final OutputStream outputStream;
  725.         private PushbackInputStream inputStream;
  726.         private int splitbyte;
  727.         private int rlen;
  728.         private String uri;
  729.         private Method method;
  730.         private Map<String, String> parms;
  731.         private Map<String, String> headers;
  732.         private CookieHandler cookies;
  733.         private String queryParameterString;
  734.         public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream) {
  735.             this.tempFileManager = tempFileManager;
  736.             this.inputStream = new PushbackInputStream(inputStream, BUFSIZE);
  737.             this.outputStream = outputStream;
  738.         }
  739.         public HTTPSession(TempFileManager tempFileManager, InputStream inputStream, OutputStream outputStream, InetAddress inetAddress) {
  740.             this.tempFileManager = tempFileManager;
  741.             this.inputStream = new PushbackInputStream(inputStream, BUFSIZE);
  742.             this.outputStream = outputStream;
  743.             String remoteIp = inetAddress.isLoopbackAddress() || inetAddress.isAnyLocalAddress() ? “127.0.0.1” : inetAddress.getHostAddress().toString();
  744.             headers = new HashMap<String, String>();
  745.             headers.put(“remote-addr”, remoteIp);
  746.             headers.put(“http-client-ip”, remoteIp);
  747.         }
  748.         @Override
  749.         public void execute() throws IOException {
  750.             try {
  751.                 /* Read the first 8192 bytes.
  752.                    The full header should fit in here.
  753.                    Apache’s default header limit is 8KB.
  754.                    Do NOT assume that a single read will get the entire header at once!
  755.                    header的最大长度为8192bytes,所以这里设置BUFSIZE为8192bytes
  756.                 */
  757.                 byte[] buf = new byte[BUFSIZE];
  758.                 splitbyte = 0;
  759.                 rlen = 0;//总共的长度
  760.                 {
  761.                     int read = -1;//每次读取的长度
  762.                     try {
  763.                         read = inputStream.read(buf, 0, BUFSIZE);
  764.                     } catch (Exception e) {
  765.                         safeClose(inputStream);
  766.                         safeClose(outputStream);
  767.                         throw new SocketException(“NanoHttpd Shutdown”);
  768.                     }
  769.                     if (read == -1) {
  770.                         // socket was been closed
  771.                         safeClose(inputStream);
  772.                         safeClose(outputStream);
  773.                         throw new SocketException(“NanoHttpd Shutdown”);
  774.                     }
  775.                     while (read > 0) {
  776.                         rlen += read;
  777.                         splitbyte = findHeaderEnd(buf, rlen);
  778.                         if (splitbyte > 0)//如果返回0则说明没有找到
  779.                             break;
  780.                         read = inputStream.read(buf, rlen, BUFSIZE – rlen);
  781.                     }
  782.                 }
  783.                 //如果读多了,则将多余的部分重新写到流里面
  784.                 if (splitbyte < rlen) {
  785.                     inputStream.unread(buf, splitbyte, rlen – splitbyte);
  786.                 }
  787.                 parms = new HashMap<String, String>();
  788.                 if(null == headers) {
  789.                     headers = new HashMap<String, String>();
  790.                 }
  791.                 // Create a BufferedReader for parsing the header.
  792.                 BufferedReader hin = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf, 0, rlen)));
  793.                 // Decode the header into parms and header java properties
  794.                 Map<String, String> pre = new HashMap<String, String>();
  795.                 decodeHeader(hin, pre, parms, headers);
  796.                 /**
  797.                  * 取出客户端请求的方式:GET/POST
  798.                  */
  799.                 method = Method.lookup(pre.get(“method”));
  800.                 if (method == null) {
  801.                     throw new ResponseException(Response.Status.BAD_REQUEST, “BAD REQUEST: Syntax error.”);
  802.                 }
  803.                 //请求的连接
  804.                 uri = pre.get(“uri”);
  805.                 cookies = new CookieHandler(headers);
  806.                 /*
  807.                  *  Ok, now do the serve()
  808.                  *  解析
  809.                  */
  810.                 Response r = serve(this);
  811.                 if (r == null) {
  812.                     throw new ResponseException(Response.Status.INTERNAL_ERROR, “SERVER INTERNAL ERROR: Serve() returned a null response.”);
  813.                 } else {
  814.                     cookies.unloadQueue(r);
  815.                     //响应客户端信息
  816.                     r.setRequestMethod(method);
  817.                     r.send(outputStream);
  818.                 }
  819.             } catch (SocketException e) {
  820.                 // throw it out to close socket object (finalAccept)
  821.                 throw e;
  822.             } catch (SocketTimeoutException ste) {
  823.                 throw ste;
  824.             } catch (IOException ioe) {
  825.                 Response r = new Response(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, “SERVER INTERNAL ERROR: IOException: ” + ioe.getMessage());
  826.                 r.send(outputStream);
  827.                 safeClose(outputStream);
  828.             } catch (ResponseException re) {
  829.                 Response r = new Response(re.getStatus(), MIME_PLAINTEXT, re.getMessage());
  830.                 r.send(outputStream);
  831.                 safeClose(outputStream);
  832.             } finally {
  833.                 tempFileManager.clear();
  834.             }
  835.         }
  836.         @Override
  837.         public void parseBody(Map<String, String> files) throws IOException, ResponseException {
  838.             RandomAccessFile randomAccessFile = null;
  839.             BufferedReader in = null;
  840.             try {
  841.                 //创建临时文件
  842.                 randomAccessFile = getTmpBucket();
  843.                 long size;
  844.                 if (headers.containsKey(“content-length”)) {
  845.                     size = Integer.parseInt(headers.get(“content-length”));
  846.                 } else if (splitbyte < rlen) {
  847.                     size = rlen – splitbyte;
  848.                 } else {
  849.                     size = 0;
  850.                 }
  851.                 /*
  852.                  *  Now read all the body and write it to f
  853.                  *  将流的内容写到临时文件中
  854.                  */
  855.                 byte[] buf = new byte[512];
  856.                 while (rlen >= 0 && size > 0) {
  857.                     rlen = inputStream.read(buf, 0, (int)Math.min(size, 512));
  858.                     size -= rlen;
  859.                     if (rlen > 0) {
  860.                         randomAccessFile.write(buf, 0, rlen);
  861.                     }
  862.                 }
  863.                 // Get the raw body as a byte []
  864.                 ByteBuffer fbuf = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, randomAccessFile.length());
  865.                 randomAccessFile.seek(0);//重新定位到临时文件的开始位置
  866.                 // Create a BufferedReader for easily reading it as string.
  867.                 InputStream bin = new FileInputStream(randomAccessFile.getFD());
  868.                 in = new BufferedReader(new InputStreamReader(bin));
  869.                 // If the method is POST, there may be parameters
  870.                 // in data section, too, read it:
  871.                 if (Method.POST.equals(method)) {
  872.                     String contentType = “”;
  873.                     String contentTypeHeader = headers.get(“content-type”);
  874.                     StringTokenizer st = null;
  875.                     if (contentTypeHeader != null) {
  876.                         st = new StringTokenizer(contentTypeHeader, “,; “);
  877.                         if (st.hasMoreTokens()) {
  878.                             contentType = st.nextToken();
  879.                         }
  880.                     }
  881.                     //表单提交,文件上传类型
  882.                     if (“multipart/form-data”.equalsIgnoreCase(contentType)) {
  883.                         // Handle multipart/form-data
  884.                         if (!st.hasMoreTokens()) {
  885.                             throw new ResponseException(Response.Status.BAD_REQUEST, “BAD REQUEST: Content type is multipart/form-data but boundary missing. Usage: GET /example/file.html”);
  886.                         }
  887.                         String boundaryStartString = “boundary=”;
  888.                         int boundaryContentStart = contentTypeHeader.indexOf(boundaryStartString) + boundaryStartString.length();
  889.                         String boundary = contentTypeHeader.substring(boundaryContentStart, contentTypeHeader.length());
  890.                         if (boundary.startsWith(“\””) && boundary.endsWith(“\””)) {
  891.                             boundary = boundary.substring(1, boundary.length() – 1);
  892.                         }
  893.                         //fbuf里面包含body的内容
  894.                         decodeMultipartData(boundary, fbuf, in, parms, files);
  895.                     } else {
  896.                         // Handle application/x-www-form-urlencoded
  897.                         String postLine = “”;
  898.                         StringBuilder postLineBuffer = new StringBuilder();
  899.                         char pbuf[] = new char[512];
  900.                         int read = in.read(pbuf);
  901.                         while (read >= 0 && !postLine.endsWith(“\r\n”)) {
  902.                             postLine = String.valueOf(pbuf, 0, read);
  903.                             postLineBuffer.append(postLine);
  904.                             read = in.read(pbuf);
  905.                         }
  906.                         postLine = postLineBuffer.toString().trim();
  907.                         decodeParms(postLine, parms);
  908.                     }
  909.                 } else if (Method.PUT.equals(method)) {
  910.                     String content = saveTmpFile(fbuf, 0, fbuf.limit());
  911.                     files.put(“content”, content);
  912.                 }
  913.             } finally {
  914.                 safeClose(randomAccessFile);
  915.                 safeClose(in);
  916.             }
  917.         }
  918.         /**
  919.          * Decodes the sent headers and loads the data into Key/value pairs
  920.          */
  921.         private void decodeHeader(BufferedReader in, Map<String, String> pre, Map<String, String> parms, Map<String, String> headers)
  922.             throws ResponseException {
  923.             try {
  924.                 // Read the request line GET / HTTP/1.1
  925.                 String inLine = in.readLine();
  926.                 if (inLine == null) {
  927.                     return;
  928.                 }
  929.                 //分隔inLine用 \t\n\r\f
  930.                 StringTokenizer st = new StringTokenizer(inLine);
  931.                 if (!st.hasMoreTokens()) {
  932.                     throw new ResponseException(Response.Status.BAD_REQUEST, “BAD REQUEST: Syntax error. Usage: GET /example/file.html”);
  933.                 }
  934.                 String method = st.nextToken();
  935.                 pre.put(“method”, method);
  936.                 if (!st.hasMoreTokens()) {
  937.                     throw new ResponseException(Response.Status.BAD_REQUEST, “BAD REQUEST: Missing URI. Usage: GET /example/file.html”);
  938.                 }
  939.                 String uri = st.nextToken();
  940.                 /*
  941.                  *  Decode parameters from the URI
  942.                  *  以”?” 来判断是否有参数
  943.                  */
  944.                 int qmi = uri.indexOf(‘?’);
  945.                 if (qmi >= 0) {//说明有参数,调用decodeParms将参数解析到parms中
  946.                     decodeParms(uri.substring(qmi + 1), parms);
  947.                     uri = decodePercent(uri.substring(0, qmi));
  948.                 } else {
  949.                     uri = decodePercent(uri);
  950.                 }
  951.                 // If there’s another token, it’s protocol version,
  952.                 // followed by HTTP headers. Ignore version but parse headers.
  953.                 // NOTE: this now forces header names lowercase since they are
  954.                 // case insensitive and vary by client.
  955.                 if (st.hasMoreTokens()) {
  956.                     String line = in.readLine();
  957.                     while (line != null && line.trim().length() > 0) {
  958.                         int p = line.indexOf(‘:’);
  959.                         if (p >= 0)
  960.                             headers.put(line.substring(0, p).trim().toLowerCase(Locale.US), line.substring(p + 1).trim());
  961.                         line = in.readLine();
  962.                     }
  963.                 }
  964.                 pre.put(“uri”, uri);
  965.             } catch (IOException ioe) {
  966.                 throw new ResponseException(Response.Status.INTERNAL_ERROR, “SERVER INTERNAL ERROR: IOException: ” + ioe.getMessage(), ioe);
  967.             }
  968.         }
  969.         /**
  970.          * Decodes the Multipart Body data and put it into Key/Value pairs.
  971.          * @param boundary
  972.          * @param fbuf
  973.          * @param in
  974.          * @param parms header头里面的参数
  975.          * @param files  支持多文件上传功能
  976.          * @throws ResponseException
  977.          */
  978.         private void decodeMultipartData(String boundary, ByteBuffer fbuf, BufferedReader in, Map<String, String> parms,
  979.                                          Map<String, String> files) throws ResponseException {
  980.             try {
  981.                 //根据boundary获取分割线与内容的间隔位置
  982.                 int[] bpositions = getBoundaryPositions(fbuf, boundary.getBytes());
  983.                 int boundarycount = 2;//源码为1,如果是上传单个文件会有问题
  984.                 String mpline = in.readLine();
  985.                 while (mpline != null) {
  986.                     if (!mpline.contains(boundary)) {
  987.                         throw new ResponseException(Response.Status.BAD_REQUEST, “BAD REQUEST: Content type is multipart/form-data but next chunk does not start with boundary. Usage: GET /example/file.html”);
  988.                     }
  989.                     boundarycount++;
  990.                     Map<String, String> item = new HashMap<String, String>();
  991.                     mpline = in.readLine();
  992.                     while (mpline != null && mpline.trim().length() > 0) {
  993.                         int p = mpline.indexOf(‘:’);
  994.                         if (p != -1) {
  995.                             item.put(mpline.substring(0, p).trim().toLowerCase(Locale.US), mpline.substring(p + 1).trim());
  996.                         }
  997.                         mpline = in.readLine();
  998.                     }
  999.                     if (mpline != null) {
  1000.                         String contentDisposition = item.get(“content-disposition”);
  1001.                         if (contentDisposition == null) {
  1002.                             throw new ResponseException(Response.Status.BAD_REQUEST, “BAD REQUEST: Content type is multipart/form-data but no content-disposition info found. Usage: GET /example/file.html”);
  1003.                         }
  1004.                         StringTokenizer st = new StringTokenizer(contentDisposition, “; “);
  1005.                         Map<String, String> disposition = new HashMap<String, String>();
  1006.                         while (st.hasMoreTokens()) {
  1007.                             String token = st.nextToken();
  1008.                             int p = token.indexOf(‘=’);
  1009.                             if (p != -1) {
  1010.                                 disposition.put(token.substring(0, p).trim().toLowerCase(Locale.US), token.substring(p + 1).trim());
  1011.                             }
  1012.                         }
  1013.                         String pname = disposition.get(“name”);
  1014.                         pname = pname.substring(1, pname.length() – 1);
  1015.                         String value = “”;
  1016.                         if (item.get(“content-type”) == null) {
  1017.                             while (mpline != null && !mpline.contains(boundary)) {
  1018.                                 mpline = in.readLine();
  1019.                                 if (mpline != null) {
  1020.                                     int d = mpline.indexOf(boundary);
  1021.                                     if (d == -1) {
  1022.                                         value += mpline;
  1023.                                     } else {
  1024.                                         value += mpline.substring(0, d – 2);
  1025.                                     }
  1026.                                 }
  1027.                             }
  1028.                         } else {
  1029.                             if (boundarycount > bpositions.length) {
  1030.                                 throw new ResponseException(Response.Status.INTERNAL_ERROR, “Error processing request”);
  1031.                             }
  1032.                             int offset = stripMultipartHeaders(fbuf, bpositions[boundarycount – 2]);
  1033.                             //创建一个临时文件,然后保存内容,这个函数我们也需要重写setTempFileManagerFactory
  1034.                             String path = saveTmpFile(fbuf, offset, bpositions[boundarycount – 1] – offset – 4);
  1035.                             files.put(pname, path);
  1036.                             value = disposition.get(“filename”);
  1037.                             value = value.substring(1, value.length() – 1);
  1038.                             do {
  1039.                                 mpline = in.readLine();
  1040.                             } while (mpline != null && !mpline.contains(boundary));
  1041.                         }
  1042.                         parms.put(pname, value);
  1043.                     }
  1044.                 }
  1045.             } catch (IOException ioe) {
  1046.                 throw new ResponseException(Response.Status.INTERNAL_ERROR, “SERVER INTERNAL ERROR: IOException: ” + ioe.getMessage(), ioe);
  1047.             }
  1048.         }
  1049.         /**
  1050.          * Find byte index separating header from body. It must be the last byte of the first two sequential new lines.
  1051.          * 找到head和body的分界点,两个回车换行
  1052.          */
  1053.         private int findHeaderEnd(final byte[] buf, int rlen) {
  1054.             int splitbyte = 0;
  1055.             while (splitbyte + 3 < rlen) {
  1056.                 if (buf[splitbyte] == ‘\r’ && buf[splitbyte + 1] == ‘\n’ && buf[splitbyte + 2] == ‘\r’ && buf[splitbyte + 3] == ‘\n’) {
  1057.                     return splitbyte + 4;
  1058.                 }
  1059.                 splitbyte++;
  1060.             }
  1061.             return 0;
  1062.         }
  1063.         /**
  1064.          * Find the byte positions where multipart boundaries start.
  1065.          * @param b
  1066.          * @param boundary
  1067.          * @return
  1068.          */
  1069.         private int[] getBoundaryPositions(ByteBuffer b, byte[] boundary) {
  1070.             int matchcount = 0;
  1071.             int matchbyte = -1;
  1072.             List<Integer> matchbytes = new ArrayList<Integer>();
  1073.             for (int i = 0; i < b.limit(); i++) {
  1074.                 if (b.get(i) == boundary[matchcount]) {
  1075.                     if (matchcount == 0)
  1076.                         matchbyte = i;
  1077.                     matchcount++;
  1078.                     if (matchcount == boundary.length) {
  1079.                         matchbytes.add(matchbyte);
  1080.                         matchcount = 0;
  1081.                         matchbyte = -1;
  1082.                     }
  1083.                 } else {
  1084.                     i -= matchcount;
  1085.                     matchcount = 0;
  1086.                     matchbyte = -1;
  1087.                 }
  1088.             }
  1089.             int[] ret = new int[matchbytes.size()];
  1090.             for (int i = 0; i < ret.length; i++) {
  1091.                 ret[i] = matchbytes.get(i);
  1092.             }
  1093.             return ret;
  1094.         }
  1095.         /**
  1096.          * Retrieves the content of a sent file and saves it to a temporary file. The full path to the saved file is returned.
  1097.          */
  1098.         private String saveTmpFile(ByteBuffer b, int offset, int len) {
  1099.             String path = “”;
  1100.             if (len > 0) {
  1101.                 FileOutputStream fileOutputStream = null;
  1102.                 try {
  1103.                     //这个地方的tempFileManager需要我们setTempFileManagerFactory
  1104.                     TempFile tempFile = tempFileManager.createTempFile();
  1105.                     ByteBuffer src = b.duplicate();
  1106.                     fileOutputStream = new FileOutputStream(tempFile.getName());
  1107.                     FileChannel dest = fileOutputStream.getChannel();
  1108.                     src.position(offset).limit(offset + len);
  1109.                     dest.write(src.slice());
  1110.                     path = tempFile.getName();
  1111.                 } catch (Exception e) { // Catch exception if any
  1112.                     System.err.println(“Error: ” + e.getMessage());
  1113.                 } finally {
  1114.                     safeClose(fileOutputStream);
  1115.                 }
  1116.             }
  1117.             return path;
  1118.         }
  1119.         private RandomAccessFile getTmpBucket() {
  1120.             try {
  1121.                 TempFile tempFile = tempFileManager.createTempFile();
  1122.                 return new RandomAccessFile(tempFile.getName(), “rw”);
  1123.             } catch (Exception e) {
  1124.                 System.err.println(“Error: ” + e.getMessage());
  1125.             }
  1126.             return null;
  1127.         }
  1128.         /**
  1129.          * It returns the offset separating multipart file headers from the file’s data.
  1130.          */
  1131.         private int stripMultipartHeaders(ByteBuffer b, int offset) {
  1132.             int i;
  1133.             for (i = offset; i < b.limit(); i++) {
  1134.                 if (b.get(i) == ‘\r’ && b.get(++i) == ‘\n’ && b.get(++i) == ‘\r’ && b.get(++i) == ‘\n’) {
  1135.                     break;
  1136.                 }
  1137.             }
  1138.             return i + 1;
  1139.         }
  1140.         /**
  1141.          * Decodes parameters in percent-encoded URI-format ( e.g. “name=Jack%20Daniels&pass=Single%20Malt” ) and
  1142.          * adds them to given Map. NOTE: this doesn’t support multiple identical keys due to the simplicity of Map.
  1143.          */
  1144.         private void decodeParms(String parms, Map<String, String> p) {
  1145.             if (parms == null) {
  1146.                 queryParameterString = “”;
  1147.                 return;
  1148.             }
  1149.             queryParameterString = parms;
  1150.             //使用&来分隔参数
  1151.             StringTokenizer st = new StringTokenizer(parms, “&”);
  1152.             while (st.hasMoreTokens()) {
  1153.                 String e = st.nextToken();
  1154.                 int sep = e.indexOf(‘=’);
  1155.                 if (sep >= 0) {
  1156.                     /*
  1157.                      * 这里使用decodePercent来解析,防止参数里面带有中文
  1158.                      * 比如“张杰”那么浏览器会写成%E5%BC%A0%E6%9D%B0
  1159.                      */
  1160.                     p.put(decodePercent(e.substring(0, sep)).trim(),
  1161.                         decodePercent(e.substring(sep + 1)));
  1162.                 } else {
  1163.                     p.put(decodePercent(e).trim(), “”);
  1164.                 }
  1165.             }
  1166.         }
  1167.         @Override
  1168.         public final Map<String, String> getParms() {
  1169.             return parms;
  1170.         }
  1171.         public String getQueryParameterString() {
  1172.             return queryParameterString;
  1173.         }
  1174.         @Override
  1175.         public final Map<String, String> getHeaders() {
  1176.             return headers;
  1177.         }
  1178.         @Override
  1179.         public final String getUri() {
  1180.             return uri;
  1181.         }
  1182.         @Override
  1183.         public final Method getMethod() {
  1184.             return method;
  1185.         }
  1186.         @Override
  1187.         public final InputStream getInputStream() {
  1188.             return inputStream;
  1189.         }
  1190.         @Override
  1191.         public CookieHandler getCookies() {
  1192.             return cookies;
  1193.         }
  1194.     }
  1195.     public static class Cookie {
  1196.         private String n, v, e;
  1197.         public Cookie(String name, String value, String expires) {
  1198.             n = name;
  1199.             v = value;
  1200.             e = expires;
  1201.         }
  1202.         public Cookie(String name, String value) {
  1203.             this(name, value, 30);
  1204.         }
  1205.         public Cookie(String name, String value, int numDays) {
  1206.             n = name;
  1207.             v = value;
  1208.             e = getHTTPTime(numDays);
  1209.         }
  1210.         public String getHTTPHeader() {
  1211.             String fmt = “%s=%s; expires=%s”;
  1212.             return String.format(fmt, n, v, e);
  1213.         }
  1214.         public static String getHTTPTime(int days) {
  1215.             Calendar calendar = Calendar.getInstance();
  1216.             SimpleDateFormat dateFormat = new SimpleDateFormat(“EEE, dd MMM yyyy HH:mm:ss z”, Locale.US);
  1217.             dateFormat.setTimeZone(TimeZone.getTimeZone(“GMT”));
  1218.             calendar.add(Calendar.DAY_OF_MONTH, days);
  1219.             return dateFormat.format(calendar.getTime());
  1220.         }
  1221.     }
  1222.     /**
  1223.      * Provides rudimentary support for cookies.
  1224.      * Doesn’t support ‘path’, ‘secure’ nor ‘httpOnly’.
  1225.      * Feel free to improve it and/or add unsupported features.
  1226.      *
  1227.      * @author LordFokas
  1228.      */
  1229.     public class CookieHandler implements Iterable<String> {
  1230.         private HashMap<String, String> cookies = new HashMap<String, String>();
  1231.         private ArrayList<Cookie> queue = new ArrayList<Cookie>();
  1232.         public CookieHandler(Map<String, String> httpHeaders) {
  1233.             String raw = httpHeaders.get(“cookie”);
  1234.             if (raw != null) {
  1235.                 String[] tokens = raw.split(“;”);
  1236.                 for (String token : tokens) {
  1237.                     String[] data = token.trim().split(“=”);
  1238.                     if (data.length == 2) {
  1239.                         cookies.put(data[0], data[1]);
  1240.                     }
  1241.                 }
  1242.             }
  1243.         }
  1244.         @Override public Iterator<String> iterator() {
  1245.             return cookies.keySet().iterator();
  1246.         }
  1247.         /**
  1248.          * Read a cookie from the HTTP Headers.
  1249.          *
  1250.          * @param name The cookie’s name.
  1251.          * @return The cookie’s value if it exists, null otherwise.
  1252.          */
  1253.         public String read(String name) {
  1254.             return cookies.get(name);
  1255.         }
  1256.         /**
  1257.          * Sets a cookie.
  1258.          *
  1259.          * @param name    The cookie’s name.
  1260.          * @param value   The cookie’s value.
  1261.          * @param expires How many days until the cookie expires.
  1262.          */
  1263.         public void set(String name, String value, int expires) {
  1264.             queue.add(new Cookie(name, value, Cookie.getHTTPTime(expires)));
  1265.         }
  1266.         public void set(Cookie cookie) {
  1267.             queue.add(cookie);
  1268.         }
  1269.         /**
  1270.          * Set a cookie with an expiration date from a month ago, effectively deleting it on the client side.
  1271.          *
  1272.          * @param name The cookie name.
  1273.          */
  1274.         public void delete(String name) {
  1275.             set(name, “-delete-“, -30);
  1276.         }
  1277.         /**
  1278.          * Internally used by the webserver to add all queued cookies into the Response’s HTTP Headers.
  1279.          *
  1280.          * @param response The Response object to which headers the queued cookies will be added.
  1281.          */
  1282.         public void unloadQueue(Response response) {
  1283.             for (Cookie cookie : queue) {
  1284.                 response.addHeader(“Set-Cookie”, cookie.getHTTPHeader());
  1285.             }
  1286.         }
  1287.     }
  1288. }

 

NanoHTTPD流程

NanoHTTPD是一个抽象类,所以要继承NanoHTTPD,然后调用start(),在start()方法里面主要实现ServerSocket,然后等待客户端的连接,当有客户端连接的时候,myServerSocket.accept()获取socket实例,然后实例化InputStream和OutputStream,然后传递给HTTPSession类,在其execute()方法中 进行解析HTTP的header和body。

header解析

header和body直接使用两个回车换行符来间隔,由于Header的最大长度为8k,所以创建了一个8k的缓冲,来读取header,splitbyte = findHeaderEnd(buf, rlen);直到splitbyte 返回值大于0,说明已经知道分隔点,如果读取的长度大于splitbyte ,则把多余的部分回写到流里面,然后创建一个BufferedReader来解析头部数据调用decodeHeader方法

body解析

对于body的解析根据请求的方法分为PUT/POST,对于POST方法,使用serve方法来解析body,而serve这个方法也是我们实现NanoHTTPD类所要重写的方法,parseBody来真正解析body,首先创建一个临时文件把流里面的数据写到这个临时文件中,然后实例化ByteBuffer和BufferedReader,如果提交表单的类型是multipart/form-data,查询boundary,而boundary=就是内容数据的分割点,使用decodeMultipartData方法分离参数和内容数据,最后调用saveTmpFile方法来保存文件,为了保存到我们的本地文件中,所以我们还需要TempFileManagerFactory和TempFileManager,TempFileManager用来创建一个本地文件,用来存储真正的上传数据。

标签