A simple web server that can be bound to either the EthernetInterface or the WiflyInterface.

Dependents:   Smart-WiFly-WebServer WattEye X10Svr SSDP_Server

Revision:
44:71f09e4255f4
Parent:
43:3fc773c2986e
Child:
46:eaa86d48be6f
--- a/SW_HTTPServer.h	Mon Feb 02 03:01:00 2015 +0000
+++ b/SW_HTTPServer.h	Sat Mar 26 20:38:59 2016 +0000
@@ -13,8 +13,8 @@
 
 /// This is the default buffer size used to send files. You might size
 /// this to be equal or less than the payload size of 1460 bytes.
-/// See User Manual 3.6.1.
-#define FILESEND_BUF_SIZE 1460
+/// For WiFly: see User Manual 3.6.1.
+#define FILESEND_BUF_SIZE 500
 
 
 /// MAX_HEADER_SIZE is the default size to contain the largest header.
@@ -44,13 +44,12 @@
 /// @code
 ///     HTTPServer svr(HTTP_SERVER_PORT, "/local", 15, 30, 10, &pc);
 ///     svr.RegisterHandler("/dyn1", SimpleDynamicPage);
-///     while (true)
-///        {
-///        svr.Poll();   // this is non blocking process, but variable execution
-///        }
+///     while (true) {
+///        svr.Poll();   // this is non blocking process, with variable execution
+///     }
 /// @endcode
 ///
-/// This web server used nweb as a starting point, but expanded well beyond there.
+/// This web server used nweb as a starting point, but expanded well beyond that.
 ///  http://www.ibm.com/developerworks/systems/library/es-nweb/sidefile1.html
 ///
 /// Given: scheme://server:port/path?query_string#fragment_id
@@ -68,7 +67,10 @@
 /// @li Provides a registration interface for dynamically generated pages that
 ///     can then interact with other hardware.
 /// @li Revised to be Non-blocking, however the execution time is variable
-///     depending on the actions being performed and can span hundreds of msec.
+///     depending on the actions being performed and can span hundreds of msec
+///     when using a WiFly module as the network interface.
+/// @li Support for filenames aliases, which permit using long filenames with
+///     the LocalFileSystem (which has an 8.3 constraint).
 ///
 /// Limitations:
 /// @li When used with Wifly network interface it supports only a single 
@@ -115,7 +117,7 @@
 /// @li 20130911 Lots of incremental changes along this way, this update
 ///              refreshes the documentation.
 ///
-/// @note Copyright © 2014 by Smartware Computing, all rights reserved.
+/// @note Copyright © 2014-2016 by Smartware Computing, all rights reserved.
 ///     Individuals may use this application for evaluation or non-commercial
 ///     purposes. Within this restriction, changes may be made to this application
 ///     as long as this copyright notice is retained. The user shall make
@@ -193,6 +195,65 @@
         ACCEPT_CONTINUE,        ///< client accepted the request, additional transactions to complete.
     } CallBackResults;
     
+    /// This is the set of header codes that are generally recognized. Note that many are
+    /// not supported, so may not be listed here.
+    typedef enum HEADERCODES {
+        // 1xx: Information
+        //Continue = 100,         ///< Server has received the headers, client should send the body.
+        //Switching = 101,        ///< Request to switch protocols - not supported.
+        //Checkpoint = 102,       ///< resume aborted PUT/POST - not supported.
+        
+        // 2xx: Successful
+        OK = 200,               ///< request is OK.
+        //Created = 201,          ///< The request has been fulfilled - not supported.
+        //Accepted = 202,         ///< The request has been accepted for processing - not supported.
+        //Non_Auth = 203,         ///< The request has been successfully processed - not supported.
+        //No_Content = 204,       ///< The request has been successfully processed, but is not returning any content
+        //Reset_Content = 205,    ///< The request has been successfully processed, but is not returning any content, 
+        //                        ///     and requires that the requester reset the document view
+        //Partial_Content = 206,  /// The server is delivering only part of the resource due to a range header sent by the client
+        
+        // 3xx: Redirection
+        //Multiple_Choices = 300, /// A link list. The user can select a link and go to that location. Maximum five addresses.
+        Moved_Permanently = 301, ///< The requested page has moved to a new URL.
+        //Found = 302,            /// The requested page has moved temporarily to a new URL 
+        //See_Other = 303,        /// The requested page can be found under a different URL
+        //Not_Modified = 304,     /// Indicates the requested page has not been modified since last requested
+        // 306 Switch Proxy    No longer used
+        // 307 Temporary Redirect  The requested page has moved temporarily to a new URL
+        // 308 Resume Incomplete   Used in the resumable requests proposal to resume aborted PUT or POST requests
+
+        // 4xx: Client Error
+        Bad_Request = 400,      ///< The request cannot be fulfilled due to bad syntax
+        Unauthorized = 401,     ///< The request was a legal request, but the server is refusing to respond to it. For use when authentication is possible but has failed or not yet been provided
+        // 402 Payment Required    Reserved for future use
+        // 403 Forbidden   The request was a legal request, but the server is refusing to respond to it
+        Not_Found = 404,        ///< The requested page could not be found but may be available again in the future
+        // Method Not Allowed 405   A request was made of a page using a request method not supported by that page
+        // Not Acceptable 406   The server can only generate a response that is not accepted by the client
+        // Proxy Auth Reqd 407    The client must first authenticate itself with the proxy
+        Request_Timeout = 408,  ///< The server timed out waiting for the request
+        // 409 Conflict    The request could not be completed because of a conflict in the request
+        // 410 Gone    The requested page is no longer available
+        // 411 Length Required The "Content-Length" is not defined. The server will not accept the request without it 
+        // 412 Precondition Failed The precondition given in the request evaluated to false by the server
+        // 413 Request Entity Too Large    The server will not accept the request, because the request entity is too large
+        // 414 Request-URI Too Long    The server will not accept the request, because the URL is too long. Occurs when you convert a POST request to a GET request with a long query information 
+        Unsupported_Media_Type = 415,       ///< The server will not accept the request, because the media type is not supported 
+        // 416 Requested Range Not Satisfiable The client has asked for a portion of the file, but the server cannot supply that portion
+        // 417 Expectation Failed  The server cannot meet the requirements of the Expect request-header field
+        
+        // 5xx: Server Error
+        // Message:    Description:
+        Server_Error = 500,             ///< A generic error message, given when no more specific message is suitable
+        // 501 Not Implemented The server either does not recognize the request method, or it lacks the ability to fulfill the request
+        // 502 Bad Gateway The server was acting as a gateway or proxy and received an invalid response from the upstream server
+        // 503 Service Unavailable The server is currently unavailable (overloaded or down)
+        // 504 Gateway Timeout The server was acting as a gateway or proxy and did not receive a timely response from the upstream server
+        // 505 HTTP Version Not Supported  The server does not support the HTTP protocol version used in the request
+        // 511 Network Authentication Required The client needs to authenticate to gain network access
+    } HeaderCodes;
+    
     /**
     * This is the prototype for custom handlers that are activated via a callback
     *
@@ -267,11 +328,56 @@
         return (const char *)webroot;
     };
 
+    
+    /**
+    * Search a haystack of name:value pairs for the needle.
+    *
+    * This is a case-sensitive search. If it cannot find an alias, 
+    * it returns the needle.
+    *
+    * This is used in the web server for conveniently mapping long filenames
+    * to short filenames if you are using the LocalFileSystem which only
+    * supports short filenames.
+    *
+    * @code
+    * char * ptr;
+    * namevalue list[] = {
+    *   {"/local/longfilename.ext",  "/local/short.ext"},
+    *   {"/local/verylongname2.ext", "/local/verylo~1.ext"},
+    *   {NULL, NULL}
+    * };
+    *
+    * ptr = FindAlias(list, "/local/verylongname2.ext");
+    * // ptr now references "/local/verylo~1.ext"
+    * @endcode
+    *
+    * @param[in] haystack is the NULL terminated namevalue pair list.
+    * @param[in] needle is a pointer to the name to find.
+    * @returns the alias (value) if the needle is found, otherwise
+    *       returns the needle.
+    */
+    const char * FindAlias(const namevalue * haystack, const char * needle);
+
+
+    /**
+    * Register a list of filename aliases to webroot.
+    *
+    * Some uses of this could be on the LocalFileSystem, which only supports
+    * an 8.3 naming convention. This API lets you register a list of
+    * name:value pairs, where the name is the long filename and the
+    * value is the corresponding short filename.
+    *
+    * @param[in] namevaluelist is a pointer to a NULL terminated long
+    *           to short filename list.
+    */
+    void RegisterFilenameAliasList(const namevalue * namevaluelist);
+
     /**
     * The process to call whenever there is free time, as this basically does
     * all the work to monitor for connections and handle replies.
     *
-    * 20130601 Renamed from ip_process to Poll
+    * Activate this API as often as you can, typically from the loop in main.
+    *
     */
     void Poll();
 
@@ -282,35 +388,70 @@
     * optional data (which must end with "\r\n"). It then sends the second newline
     * sequence that signals the end of the header.
     *
-    * @param code is the optional return code; 200 = OK, if not provided then 404 = Not found is returned
-    * @param code_text is the text to align with the code (e.g. 404, "Not Found")
-    * @param content_type is a pointer to "Content-Type: text/html\r\n" (for example)
-    * @param optional_text is a pointer to any other text that is part of the header, which must
-    *        have \r\n termination.
+    * @code
+    * svr->header(200, "OK", "Content-Type: text/html\r\n");
+    * ...
+    * svr->header(200, "OK", "Content-Type: text/html\r\n", "SpecialParam: 13\r\n");
+    * svr->header("NextParam: 14\r\n");
+    * svr->header("");  // sends the final \r\n to end the header
+    * @endcode
+    *
+    * @param[in] code is the optional return code; 200 = OK, if not provided then 404 = Not found is returned
+    * @param[in] code_text is the text to align with the code (e.g. 404, "Not Found")
+    * @param[in] content_type is a pointer to "Content-Type: text/html\r\n" (for example). The string
+    *               must have a \r\n termination. If this parameter is NULL, no alternate is substituted.
+    * @param[in] optional_text is a pointer to any other text that is part of the header, which must
+    *               have \r\n termination. It is permissible to string several header items together, 
+    *               each with the \r\n termination (which includes \r\n termination at the end).
+    *               If this parameter is NULL, a standard template response will be sent consisting
+    *               of "Max-age: 0\r\nServer: Smart_Server v0.2\r\nConnection: close\r\n\r\n"
+    *               If this parameter is not NULL, the user must call headerend(), or ensure that
+    *               the termination double (\r\n\r\n) ends the optional_text.
     */
-    void header(int code = 404, const char * code_text = "Not Found", const char * content_type = NULL, 
+    void header(HeaderCodes code = Not_Found, const char * code_text = "Not Found", const char * content_type = NULL, 
         const char * optional_text = NULL);
 
     /**
+    * Send a fragment of a header to the client.
+    *
+    * This API lets you send header information a fragment at a time. A fragment can be a single
+    * line of text, or it can be several strung together with \r\n.
+    *
+    * @param[in] partialheader is a pointer to \r\n terminated text. If partial is a pointer to NULL,
+    *           then a terminating \r\n is sent, which ends the header record.
+    */
+    void header(const char * partialheader);
+
+    /**
     * Send text to the client
     *
     * This sends the specified text to the client. If the number of bytes is not set,
     * then it calculates the number of bytes as a string. For binary transfers, the
     * number of bytes to send is required for proper operation.
     *
-    * @param msg is the text string to send
-    * @param bytes is the number of bytes to send. If not set, then strlen is calculated.
+    * @param[in] msg is the text string to send
+    * @param[in] bytes is the number of bytes to send. If not set, then strlen is calculated.
     */
     void send(const char * msg, int bytes = -1);
 
     /**
+    * Get the size of the file
+    *
+    * This returns the size of the file. If the specified file is not found, it returns zero.
+    *
+    * @param[in] filename is the file to read.
+    * @returns the size of the file, or zero if the file was not found/opened.
+    */
+    uint32_t FileSize(const char * filename);
+
+    /**
     * Send a referenced file to the client, including the header
     *
     * This sends a file from the filesystem to the client. It must be of a supported type
     * in order to properly create the header.
     *
-    * @param filename is the fully qualified path and filename
-    * @param filetype is the header information (e.g. "Content-Type: application/pdf")
+    * @param[in] filename is the fully qualified path and filename
+    * @param[in] filetype is the header information (e.g. "Content-Type: application/pdf")
     * @return true if it thinks it sent ok, false otherwise.
     */
     bool SendFile(const char * filename, const char * filetype);
@@ -325,7 +466,7 @@
     * @code
     *
     *   ...
-    *   svr.RegisterHandler("/dyn1", SimpleDynamicPage);svr.RegisterHandler("/dyn1", SimpleDynamicPage);
+    *   svr.RegisterHandler("/dyn1", SimpleDynamicPage);
     *   ...
     *
     *   bool SimpleDynamicPage(HTTPServer *svr, HTTPServer::CallBackType type, const char * path, 
@@ -368,8 +509,8 @@
     *   }
     * @endcode
     *
-    * @param path to register
-    * @param callback of type Handler
+    * @param[in] path to register
+    * @param[in] callback of type Handler
     * @return true if successfully registered
     */
     bool RegisterHandler(const char * path, Handler callback);
@@ -387,7 +528,7 @@
     *       ...
     * @endcode
     *
-    * @param filename is the filename to test, based on the extension
+    * @param[in] filename is the filename to test, based on the extension
     * @return pointer to a Content-Type string if supported, or NULL if not.
     */
     const char * GetSupportedType(const char * filename);
@@ -405,7 +546,7 @@
     * leds = atoi(svr->GetParameter("leds"));
     * @endcode
     *
-    * @param name is the name to search for
+    * @param[in] name is the name to search for
     * @return pointer to the value, or NULL
     */
     const char * GetParameter(const char * name);
@@ -413,10 +554,10 @@
     /**
     * get a pointer to a name-value pair based on the index.
     *
-    * @param index is the item being referenced
+    * @param[in] index is the item being referenced
     * @return pointer to the namevalue, or NULL
     */
-    namevalue * GetParameter(int index);
+    const namevalue * GetParameter(int index);
 
     /**
     * Get the count of query parameters from the active transaction.
@@ -441,7 +582,7 @@
     * leds = atoi(svr->GetPostParameter("leds"));
     * @endcode
     *
-    * @param name is the name to search for
+    * @param[in] name is the name to search for
     * @return pointer to the value, or NULL
     */
     const char * GetPostParameter(const char * name);
@@ -449,7 +590,7 @@
     /**
     * get a pointer to a post parameter name-value pair based on the index.
     *
-    * @param index is the item being referenced
+    * @param[in] index is the item being referenced
     * @return pointer to the namevalue, or NULL
     */
     namevalue * GetPostParameter(int index);
@@ -470,10 +611,10 @@
     * This will directly modify the referenced string. If there is a
     * #fragment_id on the end of the string, it will be removed.
     *
-    * @param qP is a pointer to a namevalue set
-    * @param qpCount is a pointer to a counter of what is in the set
-    * @param maxP is the maximum number of parameters for which space has been allocated.
-    * @param pName is a pointer to the string.
+    * @param[in] qP is a pointer to a namevalue set
+    * @param[in] qpCount is a pointer to a counter of what is in the set
+    * @param[in] maxP is the maximum number of parameters for which space has been allocated.
+    * @param[in,out] pName is a pointer to the string.
     * @returns The total number of items that have been parsed, 
     *       which can include a count from a url query string.
     */
@@ -494,16 +635,16 @@
     *
     * @note '+' is another form of space, so is converted to a space before the %xx
     *
-    * @param encoded string to be converted
+    * @param[in,out] encoded string to be converted
     */
     void UnescapeString(char * encoded);
 
     /**
     * This is used to force a connection to close.
     *
-    * This switches the module into command mode, performs the close,
-    * then switches it back to data mode. So, this is a time-expensive
-    * command.
+    * @note When WiFly is the interface, this switches the module into 
+    * command mode, performs the close, and then switches it back to data mode. 
+    * So, this is a time-expensive command.
     *
     * @returns true if successful
     */
@@ -530,7 +671,7 @@
     *
     * @param hdr is the string to search for (e.g. "Content-Length")
     *
-    * @returns pointer to the value associated with that header.
+    * @returns[in] pointer to the value associated with that header.
     * @returns NULL if the header is not found.
     */
     const char * GetHeaderValue(const char * hdr);
@@ -561,7 +702,7 @@
     * This is a diagnostic function, and gathers data on the internal
     * performance of the server, as it works various actions.
     *
-    * @param p is a pointer to a SW_PerformanceData structure to be populated
+    * @param[in] p is a pointer to a SW_PerformanceData structure to be populated
     */
     void GetPerformanceData(SW_PerformanceData * p);
 
@@ -624,9 +765,10 @@
     int handlercount;
 
     char * queryType;
-    char * queryString;         // the query string [and 'GET' data] passed on the URL (e.g. ?name1=value1&name2=value2...)
-    char * postQueryString;     // the post data
-
+    char * queryString;             // the query string [and 'GET' data] passed on the URL (e.g. ?name1=value1&name2=value2...)
+    char * postQueryString;         // the post data
+    const namevalue * filenameAliasList;  // pointer to a filename alias list
+    
     /**
     *  Extract the parameter from the record, by searching for the needle in the haystack.
     *