A simple web server that can be bound to either the EthernetInterface or the WiflyInterface.
Dependents: Smart-WiFly-WebServer WattEye X10Svr SSDP_Server
SW_HTTPServer.h@2:a29c32190037, 2013-06-02 (annotated)
- Committer:
- WiredHome
- Date:
- Sun Jun 02 18:32:05 2013 +0000
- Revision:
- 2:a29c32190037
- Parent:
- 1:54353af0d20a
- Child:
- 3:17928786bdb5
turned off debug
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
WiredHome | 1:54353af0d20a | 1 | |
WiredHome | 1:54353af0d20a | 2 | #ifndef SW_HTTPSERVER_H |
WiredHome | 1:54353af0d20a | 3 | #define SW_HTTPSERVER_H |
WiredHome | 1:54353af0d20a | 4 | #include "mbed.h" |
WiredHome | 1:54353af0d20a | 5 | //#include "MODSERIAL.h" // would like to hook in mod serial for higher performance, less blocking |
WiredHome | 1:54353af0d20a | 6 | #include "Wifly.h" |
WiredHome | 1:54353af0d20a | 7 | #include "TCPSocketServer.h" |
WiredHome | 1:54353af0d20a | 8 | #include "TCPSocketConnection.h" |
WiredHome | 1:54353af0d20a | 9 | |
WiredHome | 1:54353af0d20a | 10 | #ifdef MODSERIAL_H |
WiredHome | 1:54353af0d20a | 11 | #define PC MODSERIAL |
WiredHome | 1:54353af0d20a | 12 | #else |
WiredHome | 1:54353af0d20a | 13 | #define PC Serial |
WiredHome | 1:54353af0d20a | 14 | #endif |
WiredHome | 1:54353af0d20a | 15 | |
WiredHome | 1:54353af0d20a | 16 | /// HTTPServer is a simple web server using the WiFly module. |
WiredHome | 0:729320f63c5c | 17 | /// |
WiredHome | 0:729320f63c5c | 18 | /// Partially derived from nweb |
WiredHome | 0:729320f63c5c | 19 | /// http://www.ibm.com/developerworks/systems/library/es-nweb/sidefile1.html |
WiredHome | 0:729320f63c5c | 20 | /// |
WiredHome | 0:729320f63c5c | 21 | /// Uses a modified WiflyInterface - a number of performance issues |
WiredHome | 0:729320f63c5c | 22 | /// were identified and resolved in the local version. |
WiredHome | 0:729320f63c5c | 23 | /// |
WiredHome | 0:729320f63c5c | 24 | /// Also, if nothing visits its web page for a long time, it seems |
WiredHome | 0:729320f63c5c | 25 | /// to disable. Might be a sleep timer I'm not spotting. |
WiredHome | 0:729320f63c5c | 26 | /// |
WiredHome | 0:729320f63c5c | 27 | /// Given: scheme://domain:port/path?query_string#fragment_id |
WiredHome | 0:729320f63c5c | 28 | /// @li scheme is "http" |
WiredHome | 0:729320f63c5c | 29 | /// @li domain is whatever IP the server has |
WiredHome | 0:729320f63c5c | 30 | /// @li port is the registered port |
WiredHome | 0:729320f63c5c | 31 | /// @li /path is the reference to the file (actual or logical) on the server |
WiredHome | 0:729320f63c5c | 32 | /// @li query_string is any combination of name=value pairs |
WiredHome | 0:729320f63c5c | 33 | /// @li fragment_id is a reference to an anchor on the page |
WiredHome | 0:729320f63c5c | 34 | /// |
WiredHome | 2:a29c32190037 | 35 | /// Feature request list: |
WiredHome | 0:729320f63c5c | 36 | /// @todo make it non-blocking. |
WiredHome | 0:729320f63c5c | 37 | /// @todo hunt down lengthy operations - there seems to be a long timeout somewhere. |
WiredHome | 2:a29c32190037 | 38 | /// @todo parse the header similar to the query string, and then make |
WiredHome | 2:a29c32190037 | 39 | /// those parameters accessible - perhaps like environment vars. |
WiredHome | 0:729320f63c5c | 40 | /// @todo move part of the POST method handler to the registered handler, so |
WiredHome | 0:729320f63c5c | 41 | /// it can decide if it should allocate the needed memory. |
WiredHome | 2:a29c32190037 | 42 | /// @todo Add password capability to some web pages |
WiredHome | 0:729320f63c5c | 43 | /// @todo transform the pc serial interface to a log interface, which might |
WiredHome | 0:729320f63c5c | 44 | /// be more useful. |
WiredHome | 2:a29c32190037 | 45 | /// @todo Add ability to put WiFly in AP mode and then configuration pages |
WiredHome | 2:a29c32190037 | 46 | /// to find and join a network. |
WiredHome | 2:a29c32190037 | 47 | /// @todo Add ability to change/update SW in the WiFly module |
WiredHome | 2:a29c32190037 | 48 | /// @todo Add ability to upload a new application to the mbed |
WiredHome | 2:a29c32190037 | 49 | /// |
WiredHome | 2:a29c32190037 | 50 | /// History: |
WiredHome | 2:a29c32190037 | 51 | /// @li 20130530 Initial version |
WiredHome | 2:a29c32190037 | 52 | /// @li 20130601 Renamed ip_process to Poll |
WiredHome | 0:729320f63c5c | 53 | /// |
WiredHome | 0:729320f63c5c | 54 | /// @note Copyright © 2013 by Smartware Computing, all rights reserved. |
WiredHome | 0:729320f63c5c | 55 | /// Individuals may use this application for evaluation or non-commercial |
WiredHome | 0:729320f63c5c | 56 | /// purposes. Within this restriction, changes may be made to this application |
WiredHome | 0:729320f63c5c | 57 | /// as long as this copyright notice is retained. The user shall make |
WiredHome | 0:729320f63c5c | 58 | /// clear that their work is a derived work, and not the original. |
WiredHome | 0:729320f63c5c | 59 | /// Users of this application and sources accept this application "as is" and |
WiredHome | 0:729320f63c5c | 60 | /// shall hold harmless Smartware Computing, for any undesired results while |
WiredHome | 0:729320f63c5c | 61 | /// using this application - whether real or imagined. |
WiredHome | 0:729320f63c5c | 62 | /// |
WiredHome | 0:729320f63c5c | 63 | /// @author David Smart, Smartware Computing |
WiredHome | 0:729320f63c5c | 64 | /// |
WiredHome | 0:729320f63c5c | 65 | class HTTPServer |
WiredHome | 0:729320f63c5c | 66 | { |
WiredHome | 0:729320f63c5c | 67 | public: |
WiredHome | 0:729320f63c5c | 68 | /** |
WiredHome | 0:729320f63c5c | 69 | * namevalue pairs for parameters |
WiredHome | 0:729320f63c5c | 70 | */ |
WiredHome | 0:729320f63c5c | 71 | typedef struct { |
WiredHome | 0:729320f63c5c | 72 | char * name; |
WiredHome | 0:729320f63c5c | 73 | char * value; |
WiredHome | 0:729320f63c5c | 74 | } namevalue; |
WiredHome | 2:a29c32190037 | 75 | |
WiredHome | 2:a29c32190037 | 76 | /** |
WiredHome | 2:a29c32190037 | 77 | * Indicates the purpose of the callback |
WiredHome | 2:a29c32190037 | 78 | */ |
WiredHome | 2:a29c32190037 | 79 | typedef enum { |
WiredHome | 2:a29c32190037 | 80 | CONTENT_LENGTH_REQUEST, ///<!- ask the user if they wish to accept the data |
WiredHome | 2:a29c32190037 | 81 | DATA_TRANSFER, |
WiredHome | 2:a29c32190037 | 82 | SEND_PAGE, ///<! the activated method should now send the page |
WiredHome | 2:a29c32190037 | 83 | } CallBackType; |
WiredHome | 0:729320f63c5c | 84 | |
WiredHome | 0:729320f63c5c | 85 | /** |
WiredHome | 0:729320f63c5c | 86 | * callback prototype for custom handler |
WiredHome | 0:729320f63c5c | 87 | * |
WiredHome | 2:a29c32190037 | 88 | * This callback gets overloaded for a few purposes, which can be identified by the CallBackType parameter |
WiredHome | 2:a29c32190037 | 89 | * @li SEND_PAGE - the callback should now send the html page, using as many svr->send() as needed. |
WiredHome | 2:a29c32190037 | 90 | * When the callback returns, it should always indicate true. |
WiredHome | 2:a29c32190037 | 91 | * @li CONTENT_LENGTH_REQUEST - the server is asking the callback if it wants to receive the message, |
WiredHome | 2:a29c32190037 | 92 | * which may require significant memory. If the request is accepted, true should be returned. |
WiredHome | 2:a29c32190037 | 93 | * If the request is denied, false should be returned. |
WiredHome | 2:a29c32190037 | 94 | * |
WiredHome | 0:729320f63c5c | 95 | * @param svr is a handle to this class, so the callback has access to member functions |
WiredHome | 0:729320f63c5c | 96 | * @param params is a pointer to an array of name value pairs |
WiredHome | 0:729320f63c5c | 97 | * @paramcount is the number of parameters. |
WiredHome | 2:a29c32190037 | 98 | * @return true if command was accepted |
WiredHome | 0:729320f63c5c | 99 | */ |
WiredHome | 2:a29c32190037 | 100 | typedef bool (* Handler)(HTTPServer * svr, CallBackType type, const char *path, const namevalue *params, int paramcount); |
WiredHome | 0:729320f63c5c | 101 | |
WiredHome | 0:729320f63c5c | 102 | /** |
WiredHome | 0:729320f63c5c | 103 | * Create the HTTPServer object. |
WiredHome | 0:729320f63c5c | 104 | * |
WiredHome | 0:729320f63c5c | 105 | * @param wifly is the serial port with the wifly interface. |
WiredHome | 0:729320f63c5c | 106 | * @param port is the optional parameter for the port number to use, default is 80. |
WiredHome | 0:729320f63c5c | 107 | * @param webroot is a file system path to the root folder for the web space. |
WiredHome | 0:729320f63c5c | 108 | * @param maxparams defines the maximum number of parameters to a dynamic function (and the memory to support them). |
WiredHome | 0:729320f63c5c | 109 | * @param maxdynamicpages defines the maximum number of dynamic pages that can be registered. |
WiredHome | 0:729320f63c5c | 110 | * @param pc is the serial port for debug information (I should transform this to a log interface) |
WiredHome | 0:729320f63c5c | 111 | */ |
WiredHome | 0:729320f63c5c | 112 | HTTPServer(Wifly * wifly, int port = 80, const char * webroot = "/", int maxparams = 30, int maxdynamicpages = 10, PC * pc = NULL); |
WiredHome | 0:729320f63c5c | 113 | |
WiredHome | 0:729320f63c5c | 114 | /** |
WiredHome | 0:729320f63c5c | 115 | * destructor, which can clean up a few things |
WiredHome | 0:729320f63c5c | 116 | */ |
WiredHome | 0:729320f63c5c | 117 | ~HTTPServer(); |
WiredHome | 0:729320f63c5c | 118 | |
WiredHome | 0:729320f63c5c | 119 | /** |
WiredHome | 0:729320f63c5c | 120 | * the process to call whenever there is free time, as this basically does |
WiredHome | 0:729320f63c5c | 121 | * all the work to monitor for connections and handle replies. |
WiredHome | 2:a29c32190037 | 122 | * |
WiredHome | 2:a29c32190037 | 123 | * 20130601 Renamed from ip_process to Poll |
WiredHome | 0:729320f63c5c | 124 | */ |
WiredHome | 2:a29c32190037 | 125 | void Poll(); |
WiredHome | 0:729320f63c5c | 126 | |
WiredHome | 0:729320f63c5c | 127 | /** |
WiredHome | 0:729320f63c5c | 128 | * Send a header back to the client and automatically appends a "\r\n". Each parameter must |
WiredHome | 0:729320f63c5c | 129 | * send a "\r\n" as part of that string. |
WiredHome | 0:729320f63c5c | 130 | * |
WiredHome | 0:729320f63c5c | 131 | * @param code is the optional return code; 200 = OK, if not provided then 404 = Not found is returned |
WiredHome | 0:729320f63c5c | 132 | * @param code_text is the text to align with the code (e.g. 404, "Not Found") |
WiredHome | 0:729320f63c5c | 133 | * @param content_type is a pointer to "Content-Type: text/html\r\n" (for example) |
WiredHome | 0:729320f63c5c | 134 | * @param optional_text is a pointer to any other text that is part of the header |
WiredHome | 0:729320f63c5c | 135 | */ |
WiredHome | 0:729320f63c5c | 136 | void header(int code = 404, const char * code_text = "Not Found", const char * content_type = NULL, const char * optional_text = NULL); |
WiredHome | 0:729320f63c5c | 137 | |
WiredHome | 0:729320f63c5c | 138 | /** |
WiredHome | 0:729320f63c5c | 139 | * Send text to the client |
WiredHome | 0:729320f63c5c | 140 | * |
WiredHome | 0:729320f63c5c | 141 | * @param msg is the text string to send |
WiredHome | 0:729320f63c5c | 142 | * @param bytes is the number of bytes to send. If not set, then strlen is calculated. |
WiredHome | 0:729320f63c5c | 143 | */ |
WiredHome | 0:729320f63c5c | 144 | void send(const char * msg, int bytes = -1); |
WiredHome | 0:729320f63c5c | 145 | |
WiredHome | 0:729320f63c5c | 146 | /** |
WiredHome | 0:729320f63c5c | 147 | * Send a file to the client, including the header |
WiredHome | 0:729320f63c5c | 148 | * |
WiredHome | 0:729320f63c5c | 149 | * @param filename is the fully qualified path and filename |
WiredHome | 0:729320f63c5c | 150 | * @param filetype is the header information (e.g. "Content-Type: text/html") |
WiredHome | 0:729320f63c5c | 151 | * @return true if it thinks it sent ok, false otherwise. |
WiredHome | 0:729320f63c5c | 152 | */ |
WiredHome | 0:729320f63c5c | 153 | bool SendFile(const char * filename, const char * filetype); |
WiredHome | 0:729320f63c5c | 154 | |
WiredHome | 0:729320f63c5c | 155 | /** |
WiredHome | 0:729320f63c5c | 156 | * register a handler for a specific URL. |
WiredHome | 0:729320f63c5c | 157 | * |
WiredHome | 0:729320f63c5c | 158 | * @param path to register |
WiredHome | 0:729320f63c5c | 159 | * @param callback of type Handler |
WiredHome | 0:729320f63c5c | 160 | * @return true if successfully registered |
WiredHome | 0:729320f63c5c | 161 | */ |
WiredHome | 0:729320f63c5c | 162 | bool RegisterHandler(const char * path, Handler callback); |
WiredHome | 0:729320f63c5c | 163 | |
WiredHome | 0:729320f63c5c | 164 | /** |
WiredHome | 0:729320f63c5c | 165 | * determine if the named file is a supported type (e.g. .htm, .jpg, ...) |
WiredHome | 0:729320f63c5c | 166 | * |
WiredHome | 0:729320f63c5c | 167 | * @param filename is the filename to test, based on the extension |
WiredHome | 0:729320f63c5c | 168 | * @return pointer to a Content-Type string if supported, or NULL if not. |
WiredHome | 0:729320f63c5c | 169 | */ |
WiredHome | 0:729320f63c5c | 170 | const char * GetSupportedType(const char * filename); |
WiredHome | 0:729320f63c5c | 171 | |
WiredHome | 0:729320f63c5c | 172 | /** |
WiredHome | 0:729320f63c5c | 173 | * search the available parameters for 'name' and if found, return the 'value' |
WiredHome | 0:729320f63c5c | 174 | * |
WiredHome | 0:729320f63c5c | 175 | * @param name is the name to search for |
WiredHome | 0:729320f63c5c | 176 | * @return pointer to the value, or NULL |
WiredHome | 0:729320f63c5c | 177 | */ |
WiredHome | 0:729320f63c5c | 178 | const char * GetParameter(const char * name); |
WiredHome | 0:729320f63c5c | 179 | |
WiredHome | 0:729320f63c5c | 180 | /** |
WiredHome | 0:729320f63c5c | 181 | * parse the text string into name=value parameters. This will directly |
WiredHome | 0:729320f63c5c | 182 | * modify the referenced string. If there is a #fragment_id on the end |
WiredHome | 0:729320f63c5c | 183 | * of the string, that will be removed. |
WiredHome | 0:729320f63c5c | 184 | * |
WiredHome | 0:729320f63c5c | 185 | * @param pString is a pointer to the string. |
WiredHome | 0:729320f63c5c | 186 | */ |
WiredHome | 0:729320f63c5c | 187 | void ParseParameters(char * pString); |
WiredHome | 0:729320f63c5c | 188 | |
WiredHome | 0:729320f63c5c | 189 | /** |
WiredHome | 0:729320f63c5c | 190 | * Unescape string converts a coded string "in place" into a normal string |
WiredHome | 0:729320f63c5c | 191 | * this "This%20is%20a%20question%3F%20and%20an%20answer." |
WiredHome | 0:729320f63c5c | 192 | * becomes "This is a question? and an answer." |
WiredHome | 0:729320f63c5c | 193 | * @note '+' is another form of space, so is converted to a space before the %xx |
WiredHome | 0:729320f63c5c | 194 | * |
WiredHome | 0:729320f63c5c | 195 | * @param encoded string to be converted |
WiredHome | 0:729320f63c5c | 196 | */ |
WiredHome | 0:729320f63c5c | 197 | void UnescapeString(char * encoded); |
WiredHome | 0:729320f63c5c | 198 | |
WiredHome | 0:729320f63c5c | 199 | /** |
WiredHome | 0:729320f63c5c | 200 | * Get the IP address of the remote node to which we are connected. |
WiredHome | 0:729320f63c5c | 201 | * @caution this switches the module into, and out of, command mode |
WiredHome | 0:729320f63c5c | 202 | * which has quite a time penalty. |
WiredHome | 0:729320f63c5c | 203 | */ |
WiredHome | 0:729320f63c5c | 204 | void GetRemoteAddr(char * str, int size); |
WiredHome | 0:729320f63c5c | 205 | |
WiredHome | 0:729320f63c5c | 206 | /** |
WiredHome | 0:729320f63c5c | 207 | * used to force a connection to close |
WiredHome | 0:729320f63c5c | 208 | */ |
WiredHome | 0:729320f63c5c | 209 | void close_connection(); |
WiredHome | 0:729320f63c5c | 210 | |
WiredHome | 0:729320f63c5c | 211 | private: |
WiredHome | 0:729320f63c5c | 212 | Wifly * wifly; |
WiredHome | 0:729320f63c5c | 213 | char * webroot; |
WiredHome | 0:729320f63c5c | 214 | PC * pc; |
WiredHome | 0:729320f63c5c | 215 | Timer * timer; |
WiredHome | 0:729320f63c5c | 216 | TCPSocketServer * server; |
WiredHome | 0:729320f63c5c | 217 | TCPSocketConnection client; |
WiredHome | 0:729320f63c5c | 218 | char * rewriteWithDefaultFile(char * queryString); |
WiredHome | 0:729320f63c5c | 219 | char * rewritePrependWebroot(char * queryString); |
WiredHome | 0:729320f63c5c | 220 | int maxparams; |
WiredHome | 0:729320f63c5c | 221 | namevalue *params; |
WiredHome | 0:729320f63c5c | 222 | int paramcount; |
WiredHome | 0:729320f63c5c | 223 | |
WiredHome | 0:729320f63c5c | 224 | typedef struct { |
WiredHome | 0:729320f63c5c | 225 | char * path; |
WiredHome | 0:729320f63c5c | 226 | Handler callback; |
WiredHome | 0:729320f63c5c | 227 | } handler; |
WiredHome | 0:729320f63c5c | 228 | int maxdynamicpages; |
WiredHome | 0:729320f63c5c | 229 | handler *handlers; |
WiredHome | 0:729320f63c5c | 230 | int handlercount; |
WiredHome | 0:729320f63c5c | 231 | |
WiredHome | 0:729320f63c5c | 232 | /** |
WiredHome | 0:729320f63c5c | 233 | * Extract the message from the record, by searching for the needle |
WiredHome | 0:729320f63c5c | 234 | * the string of interest follows the needle, and may be ' ' delimited |
WiredHome | 0:729320f63c5c | 235 | * Can damage haystack while processing it. |
WiredHome | 0:729320f63c5c | 236 | * |
WiredHome | 0:729320f63c5c | 237 | * @param haystack is the record to search |
WiredHome | 0:729320f63c5c | 238 | * @param needle is the text to search for, which precedes the text to return |
WiredHome | 0:729320f63c5c | 239 | * @param string is the text following the needle |
WiredHome | 0:729320f63c5c | 240 | * @return true if it extracted something successfully |
WiredHome | 0:729320f63c5c | 241 | */ |
WiredHome | 0:729320f63c5c | 242 | bool Extract(char * rec, char * needle, char ** string); |
WiredHome | 0:729320f63c5c | 243 | |
WiredHome | 0:729320f63c5c | 244 | int HexCharToInt(char c); |
WiredHome | 0:729320f63c5c | 245 | char HexPairToChar(char * p); |
WiredHome | 0:729320f63c5c | 246 | }; |
WiredHome | 0:729320f63c5c | 247 | #endif //SW_HTTPSERVER_H |