HTTP Server upon new mbed Ethernet Interface. Based on original code by Henry Leinen.

Dependencies:   EthernetInterface mbed-rtos mbed

Fork of HTTP_server by pablo gindel

Files at this revision

API Documentation at this revision

Comitter:
pabloxid
Date:
Tue Jul 30 04:37:09 2013 +0000
Parent:
2:dc9184e97328
Child:
4:2a34139c7246
Commit message:
some fixes; post handling added

Changed in this revision

HTTPServer.cpp Show annotated file Show diff for this revision Revisions of this file
HTTPServer.h Show annotated file Show diff for this revision Revisions of this file
--- a/HTTPServer.cpp	Sun Jul 28 07:53:35 2013 +0000
+++ b/HTTPServer.cpp	Tue Jul 30 04:37:09 2013 +0000
@@ -21,7 +21,7 @@
     }
 
     INFO("Connected !");
-    //  set into blocking operation
+    // set into blocking operation
     socketServer.set_blocking (true);
 
     path = _path;
@@ -32,75 +32,83 @@
 
 int HTTPServer::poll () {
 
+    int retvalue;
+
     cliente = new TCPSocketConnection;
     cliente->set_blocking (false, TIMEOUT);
 
-    if (socketServer.accept(*cliente) < 0) {
+    retvalue = socketServer.accept (*cliente);
+
+    if (retvalue == OK) {
+
+        // a new connection was received
+        INFO("Client (IP=%s) is connected !", cliente->get_address());
+
+        msg = new HTTPMsg;     // estructura para decodificar y alojar el mensaje
+
+        retvalue = pollConnection (); // esto parsea y llena las cosas contenidas en msg
+
+        if (retvalue == OK) {
+            // Handle the request
+            INFO("Handling request !");
+            handleRequest ();
+        }
+
+        delete msg;
+
+    } else {  // retvalue == ERROR
         INFO("No connection\n");
-        return ERROR;
     }
 
-    // a new connection was received
-    INFO("Client (IP=%s) is connected !", cliente->get_address());
-
-    msg = new HTTPMsg;  // estructura para decodificar y alojar el mensaje
-
-    int c = pollConnection ();  // esto parsea y llena las cosas contenidas en msg
-
-    if (c == OK) {
-        // Handle the request
-        // cliente->set_blocking (true);
-        INFO("Handling request !");
-        handleRequest ();
-    }
-
-    delete msg;
     delete cliente;
 
     INFO("Leaving polling thread\n");
-    return c;
+    return retvalue;
+
 }
 
 int HTTPServer::pollConnection () {
 
     int received = 0;
     INFO("Waiting for new data in connection");
-    //  Try receiving request line
+
+    // Try receiving request line
     received = receiveLine ();
 
     if (received == ERROR) {
-        //  there was an error, probably the connection was closed, so close this connection as well
+        // there was an error, probably the connection was closed, so close this connection as well
         INFO("No more data available. Will close this connection now.");
         return ERROR;
     }
 
-    //  The Request has not yet been received so try it
-    received = parse ();
+    // Request has not yet been received, so try it
+    received = parseRequest ();
 
     if (received == ERROR) {
-        //  Invalid content received, so close the connection
+        // Invalid content received, so close the connection
         INFO("Invalid message received, so sending negative response and closing connection !");
         tcpsend ("HTTP/1.1 400 BadRequest\n\rContent-Length: %d\n\rContent-Type: text\n\r\n\r\n\r", 0);
         return ERROR;
     }
 
-    //  The request has been received, try receive the body
+    // Request has been received, try receive the headers section
     do {
         received = receiveLine ();
         if (received == ERROR) {return ERROR;}
-        //  First check if we received an empty line. This would indicate the end of the message or message body.
+        // First check if we received an empty line;
+        // This would indicate the end of headers section.
         if (received == EMPTY) {
-            //  there was an empty line, so we can start with performing the request
+            // there was an empty line, so the headers section is complete
             INFO("Request Header was received completely. Performing request.");
             received = 0;
             break;
         } else {
-            /*  add message body
-            if (parseHeader () != 0) {
+            // parse header field
+            if (parseHeader() != OK) {
                 WARN("Invalid message header received !");
-            }*/
+            }
         }
-    } while (received > 0);   //
+    } while (received > 0);
 
     INFO("Leaving poll function!");
     return received;
@@ -151,7 +159,7 @@
     return i;
 }
 
-int HTTPServer::parse () {
+int HTTPServer::parseRequest () {
 
     //  Check if buffer content is not long enough.
     if (strlen(buffer) < MIN_LONG) {
@@ -191,11 +199,12 @@
         }
     }
 
+    // init body section length
+    msg->body_length = 0;
+
     return OK;
 }
 
-/* esta rutina no se usa */
-
 int HTTPServer::parseHeader () {
 
     //  Check if the buffer content is too short to be meaningful
@@ -209,13 +218,19 @@
             //  touple found
             buffer[i] = 0;
             value_start = i+1;
-            msg->headers[buffer] = &buffer[value_start];
+            // headers storage is disabled; uncomment next line to enable
+            // msg->headers[buffer] = &buffer[value_start];
             INFO("Header name=\"%s\" : value=\"%s\".", buffer, &buffer[value_start]);
+            // Look for "Content-Length" header
+            if (strcmp (buffer, "Content-Length") == 0) {
+                msg->body_length = atoi(&buffer[value_start]);
+                INFO ("Body section found. Length: %i", msg->body_length);
+            }
             return OK;
         }
     }
 
-    ERR("Did not recieve a valid header : \"%s\".", buffer);
+    ERR("Did not receive a valid header : \"%s\".", buffer);
     return ERROR;
 }
 
@@ -230,16 +245,16 @@
     char* argname = NULL;
     char* valuename = NULL;
     for (int i=0; i<buflen; i++) {
-        if (args_start == -1) {  // args section not yet found
+        if (args_start == -1) { // args section not yet found
             if (uri_buffer[i] == '?') {  // starts with a question mark, so got it
                 uri_buffer[i] = 0;
                 args_start = i; //  set the start of the args section
                 INFO("Argument section found !");
             }
-        } else {                  // search arg-value touples
-            if (argname == NULL) {    //  arg-name found ?
+        } else { // search arg-value touples
+            if (argname == NULL) {    // arg-name found ?
                 if (uri_buffer[i] == '=') {
-                    //  yes, separate the arg-name
+                    // yes, separate the arg-name
                     uri_buffer[i] = 0;
                     argname = &uri_buffer[args_start];
                     value_start = i+1;
@@ -250,21 +265,17 @@
                     buffer[i] = 0;
                     valuename = &uri_buffer[value_start];
                     INFO("Argument value %s", valuename);
-                    msg->args[argname] = valuename;
-                    //  reset all indicators
+                    msg->uri_args[argname] = valuename;
+                    // reset all indicators
                     argname = NULL;
                     valuename = NULL;
                 }
             }
         }
     }
-
     return OK;
 }
 
-/* verificar qué parte de msg realmente se usa,
-   eliminar el resto, incluyendo los procesos asociados */
-
 void HTTPServer::handleRequest () {
 
     int err_;
@@ -311,17 +322,6 @@
     FILE *file = fopen(reqPath.c_str(), "r");
     if (file != NULL) {
 
-        // asigna toda la memoria dinámica disponible para 'chunk'
-        char * chunk = NULL;
-        int chunk_sz = MAX_CHUNK_SIZE;
-        while(chunk == NULL) {
-            chunk_sz /= 2;
-            chunk = (char*) malloc (chunk_sz);
-            if (chunk_sz < MIN_CHUNK_SIZE) {
-                error ("OutOfMemory");
-            }
-        }
-
         // File was found and can be returned; first determine the size
         fseek (file, 0, SEEK_END);
         int size = ftell (file);
@@ -329,16 +329,14 @@
 
         startResponse (200, size);                  // response: 200 = HTTP_Ok
         while (!feof(file) && !ferror(file)) {
-            int count = fread (chunk, 1, chunk_sz , file);
+            int count = fread (buffer, 1, CHUNK_SIZE, file);
             INFO("Processing Response (%d bytes)!", count);
-            if (cliente->send_all (chunk, count) != count) {
+            if (cliente->send_all (buffer, count) != count) {
                 WARN ("Unsent bytes left !");                  // TODO: handle filesystem errors
             }
         }
-
         INFO("Ending Response !");
 
-        free (chunk);
         fclose (file);
 
     } else {
@@ -352,6 +350,36 @@
 
 int HTTPServer::handlePostRequest() {
 
+    // Try receive the body data, if there is any
+    if (msg->body_length > 0) {
+
+        INFO("Receiving body data.");
+        if (msg->body_length > BUFFER_SIZE) {error ("OutOfMemory");}
+
+        int bytes_read = 0;
+        while (bytes_read < msg->body_length) {
+            int result = cliente->receive_all(buffer+bytes_read, msg->body_length-bytes_read);
+            if (result == ERROR) {
+                WARN("Error receiving body data.");
+                break;
+            }
+            bytes_read += result;
+        }
+
+        INFO("Body data received.");
+
+        // do something
+        // use the url_decode routine :)
+
+        INFO("Done !\n");
+        return handleGetRequest();
+            
+    } else {
+        
+        ERR("POST data not found !");
+    
+    }
+
     return 404;
 }
 
@@ -361,6 +389,9 @@
                             "Content-Type: text/html\r\n"       // TODO: handle file types
                             "Server: mbed embedded\r\n"
                             "Accessible: 1\r\n"
+                            "Pragma: no-cache\r\n"
+                            "Cache-control: no-cache;no-store\r\n"
+                            "Expires: 0\r\n"
                             "\r\n";
 
 void HTTPServer::startResponse (int returnCode, int nLen) {
@@ -390,3 +421,33 @@
     INFO("Done !");
 
 }
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//                                            UTILS                                               //
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include <ctype.h>
+
+/* Converts a hex character to its integer value */
+char from_hex (char ch) {
+  return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
+}
+
+/* Returns a url-decoded version of str */
+void url_decode (char *str) {
+  char *lee = str, *escribe = str;
+  while (*lee) {
+    if (*lee == '%') {
+      if (lee[1] && lee[2]) {
+        *escribe++ = from_hex(lee[1])<<4 | from_hex(lee[2]);
+        lee += 2;
+      }
+    } else if (*lee == '+') {
+      *escribe++ = ' ';
+    } else {
+      *escribe++ = *lee;
+    }
+    lee++;
+  }
+  *escribe = 0;
+}
--- a/HTTPServer.h	Sun Jul 28 07:53:35 2013 +0000
+++ b/HTTPServer.h	Tue Jul 30 04:37:09 2013 +0000
@@ -31,17 +31,16 @@
 #include <string>
 #include <map>
 
-#define BUFFER_SIZE     256
+#define BUFFER_SIZE     256       // all-purpose buffer
 #define TIMEOUT         800
 #define OK              0
 #define ERROR           -1
 #define EMPTY           -2
 #define MIN_LONG        3
-#define MAX_CHUNK_SIZE  512
-#define MIN_CHUNK_SIZE  128
+#define CHUNK_SIZE      256
 
 #define DEBUG 0
-#include "debug.h"
+#include "../debug.h"
 
 enum RequestType {
      HTTP_RT_GET,        /*!< GET request */
@@ -57,9 +56,11 @@
      // Contains the HTTP/1.1 or HTTP/1.0 version requested by client.
      std::string version;
      // Map of headers provided by the client in the form <HeaderName>:<HeaderValue>
-     std::map<std::string, std::string>      headers;
+     std::map<std::string, std::string> headers;
      // Map of arguments that came with the uri string
-     std::map<std::string, std::string> args;
+     std::map<std::string, std::string> uri_args;
+     // length of the body data section
+     int body_length;
 };
 
 struct RequestConfig {
@@ -77,7 +78,7 @@
     char buffer [BUFFER_SIZE];
     int pollConnection ();
     int receiveLine ();
-    int parse ();
+    int parseRequest ();
     int parseHeader ();
     int parseUriArgs (char *uri_buffer);
     void handleRequest ();
@@ -120,4 +121,7 @@
 };
 
 
+void url_decode (char *str);
+
+
 #endif //__HTTPSERVER_H__