A simple web server that can be bound to either the EthernetInterface or the WiflyInterface.
Dependents: Smart-WiFly-WebServer WattEye X10Svr SSDP_Server
Diff: SW_HTTPServer.h
- 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. *