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

Dependents:   Smart-WiFly-WebServer WattEye X10Svr SSDP_Server

Revision:
3:17928786bdb5
Parent:
2:a29c32190037
Child:
4:f34642902056
--- a/SW_HTTPServer.cpp	Sun Jun 02 18:32:05 2013 +0000
+++ b/SW_HTTPServer.cpp	Mon Jun 24 20:02:01 2013 +0000
@@ -16,8 +16,6 @@
 
 //#define DEBUG
 
-#define FILESEND_BUF_SIZE 1200 // should keep this <= the ethernet frame size
-
 const char * DEFAULT_FILENAME = "index.htm";
 
 // Header information to always send
@@ -32,22 +30,30 @@
     char *ext;
     char *filetype;
 } extensions [] = {
-    {".gif", "Content-Type: image/gif\r\n"   },
-    {".jpg", "Content-Type: image/jpeg\r\n"  },
-    {".jpeg","Content-Type: image/jpeg\r\n"  },
-    {".ico", "Content-Type: image/x-icon\r\n"},
-    {".png", "Content-Type: image/png\r\n"   },
-    {".zip", "Content-Type: image/zip\r\n"   },
-    {".gz",  "Content-Type: image/gz\r\n"    },
-    {".tar", "Content-Type: image/tar\r\n"   },
-    {".txt", "Content-Type: plain/text\r\n"  },
-    {".pdf", "Content-Type: application/pdf\r\n" },
-    {".htm", "Content-Type: text/html\r\n"   },
-    {".html","Content-Type: text/html\r\n"   },
+    {".gif", "Content-Type: image/gif\r\n"          },
+    {".jpg", "Content-Type: image/jpeg\r\n"         },
+    {".jpeg","Content-Type: image/jpeg\r\n"         },
+    {".ico", "Content-Type: image/x-icon\r\n"       },
+    {".png", "Content-Type: image/png\r\n"          },
+    {".zip", "Content-Type: image/zip\r\n"          },
+    {".gz",  "Content-Type: image/gz\r\n"           },
+    {".tar", "Content-Type: image/tar\r\n"          },
+    {".txt", "Content-Type: plain/text\r\n"         },
+    {".pdf", "Content-Type: application/pdf\r\n"    },
+    {".htm", "Content-Type: text/html\r\n"          },
+    {".html","Content-Type: text/html\r\n"          },
     {0,0}
 };
 
-HTTPServer::HTTPServer(Wifly * _wf, int port, const char * _webroot, int _maxparams, int _maxdynamicpages, PC * _pc)
+HTTPServer::HTTPServer(
+    Wifly * _wf, 
+    int port, 
+    const char * _webroot, 
+    int _maxparams, 
+    int _maxdynamicpages, 
+    PC * _pc, 
+    int _allocforheader, 
+    int _allocforfile)
 {
     wifly = _wf;
     webroot = (char *)malloc(strlen(_webroot)+1);
@@ -56,16 +62,26 @@
     maxdynamicpages = _maxdynamicpages;
     params = (namevalue *)malloc(maxparams * sizeof(namevalue));
     handlers = (handler *)malloc(maxdynamicpages * sizeof(handler));
+    headerbuffersize = _allocforheader;
+    headerbuffer = (char *)malloc(headerbuffersize);
     pc = _pc;
+    queryType = NULL;
+    queryString = NULL;
+    hostString = NULL;
+    contentLength = NULL;
+    contentType = NULL;
+    postQueryString = NULL;
     paramcount = 0;
     handlercount = 0;
+    maxheaderbytes = 0;
     server = new TCPSocketServer();
-    timer = new Timer();
-    timer->start();
     server->bind(port);
     server->listen();
-    server->set_blocking(false);
+    server->set_blocking(false, 0);
+    client.set_blocking(false, 0);
     server->accept(client);
+    ResetPerformanceData();
+    timer.start();
 }
 
 HTTPServer::~HTTPServer()
@@ -74,6 +90,11 @@
     webroot = NULL;
 }
 
+int HTTPServer::GetMaxHeaderSize()
+{
+    return maxheaderbytes;
+}
+
 bool HTTPServer::RegisterHandler(const char * path, Handler callback)
 {
     if (handlercount < maxdynamicpages && path && callback) {
@@ -99,246 +120,49 @@
 //
 void HTTPServer::Poll()
 {
-#define MAXRECSIZE 4000
-    static char buffer[MAXRECSIZE];
-    static char * bPtr = buffer;
-
-    static char * queryType = NULL;
-    static char * queryString = NULL;
-    static char * hostString = NULL;
-    static char * contentLength = NULL;
-    static char * contentType = NULL;
-    static char * postQueryString = NULL;
-    static Timer tmr;
-    typedef enum { Idle, Receiving, Reply , Waiting, Sending, WaitingToClose } state;
+    typedef enum {
+        Idle,               // waiting for a connection
+        Receiving,          // receiving data
+        Sending,            // send the response
+        WaitingToClose      // small timeout to close
+    } state;
     static state op = Idle;
+    static char * bPtr = headerbuffer;
     int n;
+    static int t_ref;       // reference point for the timer
 
     switch(op) {
-        case Reply:
-            tmr.reset();
-            tmr.start();
-            op = Waiting;
+        default:                    // not expected to arrive here
+            op = Idle;
+            break;
+        case Idle:
+            timer.reset();
+            bPtr = headerbuffer;
+            if (0 == client.receive(bPtr, 0)) {  // server->accept(client) == 0) {
+                op = Receiving;
+                t_ref = timer.read_us();
+            }
             break;
-        case Waiting:
-            if (tmr.read_ms() > 10) {
-                //pc->printf("%06d delay expired, now send.\r\n", timer->read_ms());
-                op = Sending;
+        case Receiving:
+            n = client.receive(bPtr, headerbuffersize - (bPtr - headerbuffer));
+            if (n < 0) {
+                ; // pc->printf("*** client.receive error: %d\r\n", n);
+            } else if (n) {
+                bPtr[n] = '\0';
+                if (ParseHeader(headerbuffer)) {
+                    op = Sending;
+                    t_ref = RecordPerformanceData(&perfData.Header, t_ref);
+                }
+                bPtr += n;
             }
             break;
         case Sending:
-            if (strcmp(queryType, "GET") == 0
-            ||  strcmp(queryType, "POST") == 0) {
-                if (!(queryString[0] == '.' && queryString[1] == '.')) {
-                    const char * fType;
-                    bool regHandled = false;
-
-                    // Registered Dynamic Handler
-                    // If this queryString is in the list of registered handlers, call that
-                    for (int i=0; i<handlercount; i++) {
-                        if (strcmp(handlers[i].path, queryString) == 0) {
-                            (*handlers[i].callback)(this, SEND_PAGE, queryString, params, paramcount);
-                            regHandled = true;
-                            break;      // we only execute the first one
-                        }
-                    }
-
-                    if (!regHandled) {
-                        // Otherwise, this queryString must be trying to reference a static file
-                        if (queryString[strlen(queryString)-1] == '/') {
-                            queryString = rewriteWithDefaultFile(queryString);
-                        }
-                        // see if we support this file type
-                        fType = GetSupportedType(queryString);
-                        if (fType) {
-                            queryString = rewritePrependWebroot(queryString);
-                            SendFile(queryString, fType);
-                        } else {
-                            //pc->printf("Unsupported file type %s\r\n", queryString);
-                            header(404, "Not Found", "Pragma: err - Unsupported type\r\n");
-                        }
-                    }
-                } else {
-                    //pc->printf("Unsupported path %s\r\n", queryString);
-                    header(400, "Bad Request", "Pragma: err - Unsupported path\r\n");
-                }
-            } else {
-                //pc->printf("Unsupported query type %s\r\n", queryType);
-                header(400, "Bad Request", "Pragma: err - Unsupported query type\r\n");
-            }
-            tmr.reset();
-            if (queryType) { 
-                free(queryType);
-                queryType = NULL;
-            }
-            if (queryString) {
-                free(queryString);
-                queryString = NULL;
-            }
-            if (hostString) {
-                free(hostString);
-                hostString = NULL;
-            }
-            if (contentLength) {
-                free(contentLength);
-                contentLength = NULL;
-            }
-            if (contentType) {
-                free(contentType);
-                contentType = NULL;
-            }
-            if (postQueryString) {
-                free(postQueryString);
-                postQueryString = NULL;
-            }
+            SendResponse();
             op = WaitingToClose;
+            RecordPerformanceData(&perfData.SendData, t_ref);
             break;
         case WaitingToClose:
-            if (tmr.read_ms() > 10) {
-                //pc->printf("closing after %d\r\n", tmr.read_ms());
-                close_connection();
-                op = Idle;
-            }
-            break;
-        case Idle:
-            //if (server->accept(client) == 0)
-            //    op = Receiving;
-            //break;
-            // Idle and Receiving are the same until I figure out the accept method
-            // so it doesn't eat a few chars after the *OPEN*
-        case Receiving:
-            n = client.receive(bPtr, sizeof(buffer) - (bPtr - buffer));
-            if (n < 0) {
-                pc->printf("*** client.receive error: %d\r\n", n);
-            } else if (n) {
-                char * dblCR;
-                bPtr[n] = '\0';
-                wifly->setConnectionState(true);       // Bad hack to have to do this here....
-                //pc->printf("%s", bPtr);
-                // Buffer could have partial, but the double \r\n is the key
-                //      *OPEN*QueryType QueryString HTTP/1.1....
-                //          QueryType:= GET
-                //      *OPEN*GET /QueryString HTTP/1.1\r\n
-                //      *OPEN*GET /QueryString HTTP/1.1\r\nHeader stuf
-                //      *OPEN*GET /QueryString HTTP/1.1\r\nHeader stuff...\r\n\r\n
-                dblCR = strstr(buffer,"\r\n\r\n");
-                if (dblCR) {  // Have to scan from the beginning in case split on \r
-                    #ifdef DEBUG
-                    pc->printf("\r\n\r\nThe Header:\r\n%s\r\n\r\n", buffer);
-                    #endif
-                    char * soRec = buffer;                  // start of the next record of text
-                    char * eoRec = strchr(soRec, '\n');     // search for end of a record
-                    while (eoRec) {
-                        *eoRec = '\0';
-                        if (*(eoRec-1) == '\r')
-                            *(eoRec-1) = '\0';
-                        if (!Extract(soRec, "*OPEN*", &queryType))
-                            Extract(soRec, "*CLOS*", &queryType);
-                        if (queryType)
-                            Extract(soRec, queryType, &queryString);
-                        Extract(soRec, "Host: ", &hostString);
-                        Extract(soRec, "Content-Length: ", &contentLength);
-                        Extract(soRec, "Content-Type: ", &contentType);
-                        soRec = eoRec + 1;
-                        eoRec = strchr(soRec, '\n');
-                    }
-                    if (queryString) {
-                        // We have enough to try to reply
-                        //pc->printf("create reply queryType{%s}, queryString{%s}\r\n", queryType, queryString);
-                        // parse params - if any
-                        // /file.htm?name1=value1&name2=value2...
-                        // /file.htm?name1&name2=value2...
-                        paramcount = 0;
-                        char * paramDelim = strchr(queryString, '?');
-                        if (paramDelim) {
-                            *paramDelim++ = '\0';
-                            UnescapeString(paramDelim);   // everything after the '?'
-                            ParseParameters(paramDelim);    // pointing at the NULL, but there are params beyond
-                        }
-                        //for (int i=0; i<paramcount; i++)
-                        //    pc->printf("param %d '%s'='%s'\r\n", i, params[i].name, params[i].value);
-                    } else {
-                        pc->printf("not found\r\n");
-                    }
-                    op = Reply;
-                    buffer[0] = 0;
-                    bPtr = buffer;
-                    
-                    // This part parses the extra data on a POST method.
-                    // Since there has to be a dynamic handler registered for this
-                    // it would make sense to move some of this responsibility to
-                    // that handler. It could then choose if it wanted to allocate
-                    // the requested 'Content-Length' amount of memory.
-                    // Should we check the 'Content-Type' to see if it is 
-                    //   'application/x-www-form-urlencoded'?
-                    int postBytes = atoi(contentLength);
-                    bool acceptIt = false;
-                    //pc->printf("Content-Length = %d\r\n", postBytes);
-                    if (strcmp(queryType, "POST") == 0 && postBytes > 0 ) {
-                        if (postBytes) {
-                            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, params, paramcount);
-                                    regHandled = true;
-                                    break;      // we only execute the first one
-                                }
-                            }
-
-                            if (regHandled && acceptIt) {
-                                // If so, we'll make space for it
-                                postQueryString = (char *)malloc(postBytes + 1);
-                                if (postQueryString) {
-                                    char * offset;
-                                    int len;
-                                    
-                                    dblCR += 4;                         // If we slurped up any of the POST,
-                                    while (*dblCR && *dblCR <= ' ')
-                                        dblCR++;
-                                    strcpy(postQueryString, dblCR);     // copy that in and then get the rest
-                                    while ((len = strlen(postQueryString)) < postBytes) {
-                                        int n;
-                                        offset = postQueryString + len;
-                                        n = client.receive(offset, postBytes - len);
-                                        if (n >=0) {
-                                            offset[n] = '\0';
-                                        }
-                                    }
-                                    if (len >= 0) {
-                                        #ifdef DEBUG
-                                        pc->printf("POSTDATA: %s\r\n", postQueryString);
-                                        #endif
-                                        UnescapeString(postQueryString);
-                                        ParseParameters(postQueryString);
-                                    }
-                                }
-                            } else {
-                                // Simply copy it to the bitbucket
-                                int bytesToDump = postBytes;
-                                char * bitbucket = (char *)malloc(201);
-                                dblCR += 4;
-                                while (*dblCR && *dblCR <= ' ')
-                                        dblCR++;
-                                bytesToDump -= strlen(dblCR);
-                                while (bytesToDump > 0) {
-                                    int n = (bytesToDump > 200) ? 200 : bytesToDump;
-                                    n = client.receive(bitbucket, n);
-                                    bytesToDump -= n;
-                                }
-                                free(bitbucket);
-                            }
-                        }
-                    }
-                } else {
-                    // received partial, but not the double (\r\n\r\n)
-                    bPtr += n;
-                }
-            }
-            break;
-        default:
-            // not expected to arrive here
+            close_connection();
             op = Idle;
             break;
     }
@@ -360,40 +184,39 @@
     return NULL;
 }
 
+
 void HTTPServer::send(const char * msg, int bytes)
 {
     if (bytes == -1)
         bytes = strlen(msg);
     wifly->send(msg, bytes);
-#ifdef DEBUG
-//    pc->printf("%s", msg);
-#endif
 }
 
+
 bool HTTPServer::SendFile(const char * filename, const char * filetype)
 {
     FILE * fp;
-    
+
     fp  = fopen(filename,"rb");
-    // is supported, now try to open it
     if (fp) { // can open it
         char *fbuffer = (char *)malloc(FILESEND_BUF_SIZE);
         int bytes;
 
-        //pc->printf("sending [%s]\r\n", filename);
-        header(200, "OK", filetype);
-        // now the file itself
-        bytes = fread(fbuffer,sizeof(char),FILESEND_BUF_SIZE,fp);
-        while (bytes > 0) {
-            send(fbuffer, bytes);
+        if (fbuffer) {
+            header(200, "OK", filetype);
             bytes = fread(fbuffer,sizeof(char),FILESEND_BUF_SIZE,fp);
+            while (bytes > 0) {
+                send(fbuffer, bytes);
+                bytes = fread(fbuffer,sizeof(char),FILESEND_BUF_SIZE,fp);
+            }
+            free(fbuffer);
+        } else {
+            header(500, "Server Error", "Pragma: err - insufficient memory\r\n");
         }
-        free(fbuffer);
         fclose(fp);
         return true;
     } else {
-        //pc->printf("Can't open %s\r\n", filename);
-        header(404, "Not Found", "Pragma: err - Can't open\r\n");
+        header(404, "Not Found", "Pragma: err - Can't open file\r\n");
         return false;
     }
 }
@@ -455,7 +278,7 @@
 
 // this=that&who=what&more=stuff...
 // ^   ^    ^
-void HTTPServer::ParseParameters(char * pName) 
+void HTTPServer::ParseParameters(char * pName)
 {
     char * pVal;
     char * pNextName;
@@ -463,7 +286,7 @@
     // Parse params
     pVal = strchr(pName, '#');      // If there is a '#fragment_id', we can ignore it
     if (pVal)
-        *pVal = '\0';    
+        *pVal = '\0';
     do {
         //pc->printf("Parse(%s)\r\n", pName);
         //*pName++ = '\0';   // already '\0' on the first entry
@@ -494,7 +317,11 @@
     bool res = false;
     char *p;
 
-    res = wifly->sendCommand("show z\r", "NO", str, 5000);
+    if (size < 16) {        // Can only guard it here w/o modifying Wifly class
+        *str = '\0';
+        return;
+    }
+    res = wifly->sendCommand("show z\r", "NO", str, 500);
     wait(0.2);  // how long to wait is fragile, but need the rest of the chars to come in...
     if (res) {
         p = strchr(str, '\n');  // truncate after the octets.
@@ -599,4 +426,221 @@
     }
 }
 
+bool HTTPServer::CheckDynamicHandlers()
+{
+    bool regHandled = false;
 
+    // If this queryString is in the list of registered handlers, call that
+    for (int i=0; i<handlercount; i++) {
+        if (strcmp(handlers[i].path, queryString) == 0) {
+            (*handlers[i].callback)(this, SEND_PAGE, queryString, params, paramcount);
+            regHandled = true;
+            break;      // we only execute the first one
+        }
+    }
+    return regHandled;
+}
+
+void HTTPServer::SendResponse()
+{
+    if (strcmp(queryType, "GET") == 0 ||  strcmp(queryType, "POST") == 0) {
+        if (!(queryString[0] == '.' && queryString[1] == '.')) {
+            const char * fType;
+
+            if (!CheckDynamicHandlers()) {
+                // Otherwise, this queryString must be trying to reference a static file
+                if (queryString[strlen(queryString)-1] == '/') {
+                    queryString = rewriteWithDefaultFile(queryString);
+                }
+                // see if we support this file type
+                fType = GetSupportedType(queryString);
+                if (fType) {
+                    queryString = rewritePrependWebroot(queryString);
+                    SendFile(queryString, fType);
+                } else {
+                    //pc->printf("Unsupported file type %s\r\n", queryString);
+                    header(404, "Not Found", "Pragma: err - Unsupported type\r\n");
+                }
+            }
+        } else {
+            //pc->printf("Unsupported path %s\r\n", queryString);
+            header(400, "Bad Request", "Pragma: err - Unsupported path\r\n");
+        }
+    } else {
+        //pc->printf("Unsupported query type %s\r\n", queryType);
+        header(400, "Bad Request", "Pragma: err - Unsupported query type\r\n");
+    }
+    if (queryType) {
+        free(queryType);
+        queryType = NULL;
+    }
+    if (queryString) {
+        free(queryString);
+        queryString = NULL;
+    }
+    if (hostString) {
+        free(hostString);
+        hostString = NULL;
+    }
+    if (contentLength) {
+        free(contentLength);
+        contentLength = NULL;
+    }
+    if (contentType) {
+        free(contentType);
+        contentType = NULL;
+    }
+    if (postQueryString) {
+        free(postQueryString);
+        postQueryString = NULL;
+    }
+}
+
+bool HTTPServer::ParseHeader(char * buffer)
+{
+    char * dblCR;
+    bool advanceState = false;
+    int bytecount;
+    
+    // Bad hack to have to do this here, but it isn't being set in the
+    // underlying layer, and this is what allows it to properly "close"
+    // when it is done.
+    wifly->setConnectionState(true);
+    // Buffer could have partial, but the double \r\n is the key
+    //      *OPEN*QueryType QueryString HTTP/1.1....
+    //          QueryType:= GET
+    //      *OPEN*GET /QueryString HTTP/1.1\r\n
+    //      *OPEN*GET /QueryString HTTP/1.1\r\nHeader stuf
+    //      *OPEN*GET /QueryString HTTP/1.1\r\nHeader stuff...\r\n\r\n
+    dblCR = strstr(buffer,"\r\n\r\n");
+    if (dblCR) {  // Have to scan from the beginning in case split on \r
+#if 0
+        pc->printf("\r\n\r\nThe Header:\r\n%s\r\n\r\n", buffer);
+#endif
+        char * soRec = buffer;                  // start of the next record of text
+        char * eoRec = strchr(soRec, '\n');     // search for end of a record
+        
+        bytecount = strlen(buffer);
+        if (bytecount > maxheaderbytes)
+            maxheaderbytes = bytecount;
+        while (eoRec) {
+            *eoRec = '\0';
+            if (*(eoRec-1) == '\r')
+                *(eoRec-1) = '\0';
+            if (!Extract(soRec, "*OPEN*", &queryType))
+                Extract(soRec, "*CLOS*", &queryType);
+            if (queryType)
+                Extract(soRec, queryType, &queryString);
+            Extract(soRec, "Host: ", &hostString);
+            Extract(soRec, "Content-Length: ", &contentLength);
+            Extract(soRec, "Content-Type: ", &contentType);
+            soRec = eoRec + 1;
+            eoRec = strchr(soRec, '\n');
+        }
+        if (queryString) {
+            // We have enough to try to reply
+            //pc->printf("create reply queryType{%s}, queryString{%s}\r\n", queryType, queryString);
+            // parse params - if any
+            // /file.htm?name1=value1&name2=value2...
+            // /file.htm?name1&name2=value2...
+            paramcount = 0;
+            char * paramDelim = strchr(queryString, '?');
+            if (paramDelim) {
+                *paramDelim++ = '\0';
+                UnescapeString(paramDelim);   // everything after the '?'
+                ParseParameters(paramDelim);    // pointing at the NULL, but there are params beyond
+            }
+            //for (int i=0; i<paramcount; i++)
+            //    pc->printf("param %d '%s'='%s'\r\n", i, params[i].name, params[i].value);
+        } else {
+            pc->printf("not found\r\n");
+        }
+        advanceState = true;
+        buffer[0] = 0;
+//       bPtr = buffer;
+
+        // This part parses the extra data on a POST method.
+        // Since there has to be a dynamic handler registered for this
+        // it would make sense to move some of this responsibility to
+        // that handler. It could then choose if it wanted to allocate
+        // the requested 'Content-Length' amount of memory.
+        // Should we check the 'Content-Type' to see if it is
+        //   'application/x-www-form-urlencoded'?
+        int postBytes = atoi(contentLength);
+        bool acceptIt = false;
+        //pc->printf("Content-Length = %d\r\n", postBytes);
+        if (strcmp(queryType, "POST") == 0 && postBytes > 0 ) {
+            if (postBytes) {
+                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, params, paramcount);
+                        regHandled = true;
+                        break;      // we only execute the first one
+                    }
+                }
+
+                if (regHandled && acceptIt) {
+                    // If so, we'll make space for it
+                    postQueryString = (char *)malloc(postBytes + 1);
+                    if (postQueryString) {
+                        char * offset;
+                        int len;
+
+                        dblCR += 4;                         // If we slurped up any of the POST,
+                        while (*dblCR && *dblCR <= ' ')
+                            dblCR++;
+                        strcpy(postQueryString, dblCR);     // copy that in and then get the rest
+                        while ((len = strlen(postQueryString)) < postBytes) {
+                            int n;
+                            offset = postQueryString + len;
+                            n = client.receive(offset, postBytes - len);
+                            if (n >=0) {
+                                offset[n] = '\0';
+                            }
+                        }
+                        if (len >= 0) {
+                            UnescapeString(postQueryString);
+                            ParseParameters(postQueryString);
+                        }
+                    }
+                } else {
+                    // Simply copy it to the bitbucket
+                    int bytesToDump = postBytes;
+                    char * bitbucket = (char *)malloc(201);
+                    dblCR += 4;
+                    while (*dblCR && *dblCR <= ' ')
+                        dblCR++;
+                    bytesToDump -= strlen(dblCR);
+                    while (bytesToDump > 0) {
+                        int n = (bytesToDump > 200) ? 200 : bytesToDump;
+                        n = client.receive(bitbucket, n);
+                        bytesToDump -= n;
+                    }
+                    free(bitbucket);
+                }
+            }
+        }
+    }
+    return advanceState;
+}
+
+void HTTPServer::GetPerformanceData(SW_PerformanceData * p) {
+    memcpy(p, &perfData, sizeof(perfData));
+}
+
+int HTTPServer::RecordPerformanceData(SW_PerformanceParam * param, int refTime) {
+    int t_now = timer.read_us();
+    param->TotalTime_us += (t_now - refTime);
+    param->Samples++;
+    if ((t_now - refTime) > param->MaxTime_us)
+        param->MaxTime_us = (t_now - refTime);
+    return t_now;
+}
+
+void HTTPServer::ResetPerformanceData() {
+    memset(&perfData, 0, sizeof(perfData));
+}
+