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

Dependents:   Smart-WiFly-WebServer WattEye X10Svr SSDP_Server

Files at this revision

API Documentation at this revision

Comitter:
WiredHome
Date:
Thu Oct 10 13:44:07 2013 +0000
Parent:
21:660143f20b04
Child:
23:6963f45e950a
Commit message:
documentation updates.

Changed in this revision

SW_HTTPServer.cpp Show annotated file Show diff for this revision Revisions of this file
SW_HTTPServer.h Show annotated file Show diff for this revision Revisions of this file
--- a/SW_HTTPServer.cpp	Thu Sep 26 23:47:50 2013 +0000
+++ b/SW_HTTPServer.cpp	Thu Oct 10 13:44:07 2013 +0000
@@ -87,6 +87,8 @@
     wifly = _wf;
     webroot = (char *)malloc(strlen(_webroot)+1);
     strcpy(webroot, _webroot);
+    if (strlen(webroot)>1 && webroot[strlen(webroot)-1] == '/') // remove trailing '/'
+        webroot[strlen(webroot)-1] = '\0';
     maxqueryParams = _maxqueryParams;
     maxdynamicpages = _maxdynamicpages;
     headerParams = (namevalue *)malloc(maxheaderParams * sizeof(namevalue));
@@ -457,13 +459,17 @@
 
 char * HTTPServer::rewritePrependWebroot(char * queryString)
 {
-    char * temp = (char *)mymalloc(strlen(webroot) + strlen(queryString) + 1);
+    char * temp = (char *)mymalloc(strlen(webroot) + strlen(queryString) + 2); // room for /
 
     if (temp) {
         *temp = '\0';
+        char lastchar;
         strcpy(temp, webroot);
-        if (temp[strlen(temp)-1] == '/' && *queryString == '/')
-            temp[strlen(temp)-1] = '\0';
+        lastchar = temp[strlen(temp)-1];
+        if (lastchar != '/' && *queryString != '/')
+            strcat(temp, "/");
+        else if (lastchar == '/' && *queryString == '/')
+            queryString++;
         strcat(temp, queryString);
         myfree(queryString);
         return temp;
@@ -637,18 +643,26 @@
         bool acceptIt = false;
         if (strcmp(queryType, "POST") == 0 && postBytes > 0 ) {
             if (postBytes) {
+                int ndxHandler = 0;
                 bool regHandled = false;
                 // Registered Dynamic Handler
                 // Callback and ask if they want to accept this data
-                for (int i=0; i<handlercount; i++) {
-                    if (strcmp(handlers[i].path, queryString) == 0) {
-                        acceptIt = (*handlers[i].callback)(this, CONTENT_LENGTH_REQUEST, queryString, queryParams, queryParamCount);
+                for (ndxHandler=0; ndxHandler<handlercount; ndxHandler++) {
+                    if (strcmp(handlers[ndxHandler].path, queryString) == 0) {
+                        acceptIt = (*handlers[ndxHandler].callback)(this, CONTENT_LENGTH_REQUEST, queryString, queryParams, queryParamCount);
                         regHandled = true;
                         break;      // we only execute the first one
                     }
                 }
 
                 if (regHandled && acceptIt) {
+                    // @todo need to refactor - if the thing is bigger than the buffer,
+                    //       then we can receive it a chunk at a time, and hand off
+                    //       the chunks to the callback. May need callbacks that
+                    //       are: START: extract the filename/object name,
+                    //            NEXT:  a chunk of data,
+                    //            END:   signals that all chunks were delivered.
+                    //
                     // If so, we'll make space for it
                     postQueryString = (char *)mymalloc(postBytes + 1);
                     if (postQueryString) {
@@ -665,12 +679,22 @@
                             n = client.receive(offset, postBytes - len);
                             if (n >=0) {
                                 offset[n] = '\0';
+                                //printf("HTTPd: %d of %d: [%s]\r\n", len, postBytes, offset);
+                            } else if (n < 0) {
+                                //printf("HTTPd: n=%d\r\n", n);
+                                break;      // no more data, before the plan
                             }
                         }
                         if (len >= 0) {
-                            UnescapeString(postQueryString);
-                            ParseParameters(postQueryString);
+//                            UnescapeString(postQueryString);
+//                            ParseParameters(postQueryString);
+                            // use the same handler as for the length check
+                            acceptIt = (*handlers[ndxHandler].callback)(this, DATA_TRANSFER, postQueryString, NULL, 0);
+                        } else {
+                            pc->printf("HTTPd: len error.\r\n");
                         }
+                    } else {
+                        pc->printf("HTTPd: attempt to allocate %d failed.\r\n", postBytes+1);
                     }
                 } else {
                     // Simply copy it to the bitbucket
--- a/SW_HTTPServer.h	Thu Sep 26 23:47:50 2013 +0000
+++ b/SW_HTTPServer.h	Thu Oct 10 13:44:07 2013 +0000
@@ -51,7 +51,7 @@
 /// or signaling outputs.
 ///
 /// @code
-///     HTTPServer svr(&wifly, HTTP_SERVER_PORT, "/local/", 15, 30, 10, &pc);
+///     HTTPServer svr(&wifly, HTTP_SERVER_PORT, "/local", 15, 30, 10, &pc);
 ///     svr.RegisterHandler("/dyn1", SimpleDynamicPage);
 ///     while (true)
 ///        {
@@ -187,7 +187,7 @@
     */
     typedef enum CALLBACKTYPE {
         CONTENT_LENGTH_REQUEST, ///< ask the client if they wish to accept the data, typically from a POST event
-        DATA_TRANSFER,          ///< not currently used, may allow "chunking" the data to the client
+        DATA_TRANSFER,          ///< used when submitting a file via a form
         SEND_PAGE,              ///< the activated method should now send the page
     } CallBackType;
 
@@ -195,14 +195,23 @@
     * This is the prototype for custom handlers that are activated via a callback
     *
     * This callback gets overloaded for a few purposes, which can be identified by the \see CallBackType parameter
+    * @li CONTENT_LENGTH_REQUEST - the server is asking the callback if it wants to receive the message,
+    *       which may require significant memory. If the request is accepted, true should be returned.
+    *       If the request is denied, false should be returned.
+    * @li DATA_TRANSFER - the server is handing off a large body of data, which was accepted based
+    *       on the CONTENT_LENGTH_REQUEST callback. The data is now available for processing.
+    *       The callback should return true to continue the processing.
     * @li SEND_PAGE - the callback should now send the html page, using as many svr->send() as needed.
     *       When the callback returns, it should always indicate true.
-    * @li CONTENT_LENGTH_REQUEST - the server is asking the callback if it wants to receive the message,
-    *        which may require significant memory. If the request is accepted, true should be returned.
-    *        If the request is denied, false should be returned.
+    *
+    * @note The queryParams pointer purpose depends on the callback type.
+    *       For CONTENT_LENGTH_REQUEST, the pointer points to the name=value pairs from the 
+    *           header.
+    *       For DATA_TRANSFER, the pointer points to the start of the actual data.
+    *       For SEND_PAGE, 
     *
     * @param svr is a handle to this class, so the callback has access to member functions
-    * @param queryParams is a pointer to an array of name value pairs
+    * @param queryParams is a pointer based on the callback type.
     * @queryParamCount is the number of parameters.
     * @return true if command was accepted
     */
@@ -231,6 +240,20 @@
     ~HTTPServer();
 
     /**
+    * Get the path to the webroot, for applications that need to 
+    * reference the file system relative to that point.
+    *
+    * @note The returned value may not be exactly as set at instantiation
+    *       as trailing '/' were removed (unless the web root == "/").
+    *       e.g. "/msc/web/" becomes "/msc/web"
+    *
+    * @returns pointer to the webroot string.
+    */
+    const char * GetWebRoot() {
+        return (const char *)webroot;
+    };
+    
+    /**
     * The process to call whenever there is free time, as this basically does
     * all the work to monitor for connections and handle replies.
     *