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

Revision:
0:fcceff3299be
Child:
2:dc9184e97328
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HTTPServer.cpp	Fri Jul 26 22:05:19 2013 +0000
@@ -0,0 +1,417 @@
+#include "mbed.h"
+#include "HTTPServer.h"
+
+#define DEBUG
+#include "debug.h"
+
+#define TIMEOUT      500
+#define OK           0
+#define ERROR        -1
+#define EMPTY        -2
+#define MIN_LONG     3
+
+
+RequestConfig rq_conf[] = {
+    { "GET",    HTTP_RT_GET },
+    { "POST",   HTTP_RT_POST}
+};
+
+HTTPServer::HTTPServer (int port, const char* _path) {
+
+    INFO("Binding to port %d...", port);
+    if (socketServer.bind (port) < 0) {
+       ERR("Failed to bind to port !\n");
+       error("Binding");
+    }
+
+    INFO("Listening ...");
+    if (socketServer.listen(1) < 0) {
+       ERR("Failed to listen !\n");
+       error("Listening");
+    }
+
+    INFO("Connected !");
+    //  set into blocking operation
+    socketServer.set_blocking (true, TIMEOUT);
+
+    path = _path;
+
+}
+
+HTTPServer::~HTTPServer() { };
+
+int HTTPServer::poll () {
+
+    cliente = new TCPSocketConnection;
+    cliente->set_blocking (false, TIMEOUT);
+
+    if (socketServer.accept(*cliente) < 0) {
+        INFO("No connection\n");
+        return ERROR;
+    }
+
+    //   a new connection was received
+    INFO("Client (IP=%s) is connected !\n", 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
+        INFO("Handling request !");
+        //cliente->set_blocking (true, TIMEOUT);
+        handleRequest ();
+    }
+
+    delete msg;
+    delete cliente;
+
+    INFO("Leaving polling thread");
+    return c;
+}
+
+int HTTPServer::pollConnection () {
+
+    int received = 0;
+    INFO("Waiting for new data in connection");
+    //  Try receiving request line
+    received = receiveLine ();
+
+    if (received == ERROR) {
+        //  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 ();
+
+    if (received == ERROR) {
+        //  Invalid content received, so close the connection
+        INFO("Invalid message received, so sending negative response and closing connection !");
+        sprintf (buffer,"HTTP/1.1 400 BadRequest\n\rContent-Length: %d\n\rContent-Type: text\n\r\n\r\n\r",0);
+
+        cliente->send (buffer, strlen (buffer));
+
+        return ERROR;
+    }
+
+    /*  The request has been received, try receive the body
+    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.
+        if (received == EMPTY) {
+            //  there was an empty line, so we can start with performing the request
+            INFO("Request Header was received completely. Performing request.");
+            received = 0;
+            break;
+        } else {
+            //  add message body
+            if (parseHeader () != 0) {
+                WARN("Invalid message header received !");
+            }
+        }
+    } while (received > 0);   */
+
+    INFO("Leaving poll function!");
+    return received;
+}
+
+int HTTPServer::receiveLine () {
+
+    buffer[0] = 0;
+
+    if (!cliente->is_connected()) {
+        error("NOT Connected anymore");
+        return ERROR;
+    }
+
+    Timer tm;
+    int i;
+
+    //  Try to receive up to the max number of characters
+    for (i=0; i<BUFFER_SIZE-1; i++) {
+        int c = cliente->receive (buffer+i, 1);
+        //  Check that - if no character was currently received - the timeout period is reached.
+        if (c == 0 || c == -1) {
+            //  no character was read, so check if operation timed out
+            if (tm.read_ms() > TIMEOUT) {
+                //  Operation timed out
+                INFO("Timeout occured in function 'receiveLine'.");
+                return ERROR;
+            }
+        }
+        //  Check if line terminating character was received
+        if (buffer[i] == '\n') {break;}
+    }
+    //  Terminate with \0
+    buffer[i] = 0;
+
+    //  Trim for '\r' linefeed at the end
+    if (i>0 && buffer[i-1] == '\r') {
+        i--;
+        buffer[i] = 0;
+    }
+
+    //  return number of characters received in the line or return -2 if an empty line was received
+    if (i==0 || (i==1 && buffer[0]=='\r')) {
+        //  empty line received, so return -2
+        return EMPTY;
+    }
+    // retorna número de caracteres leidos
+    return i;
+}
+
+int HTTPServer::parse () {
+
+    //  Check if buffer content is not long enough.
+    if (strlen(buffer) < MIN_LONG) {
+        ERR("Buffer content is invalid or too short.");
+        return ERROR;
+    }
+
+    std::vector<std::string> args;
+
+    int argno = 0;
+    //  decompose string into a list of arguments
+    int start = 0; // current starting char
+    int nLen = strlen(buffer)+1;
+    for (int i=0; i<nLen; i++) {
+        if ((buffer[i] == ' ') || (buffer[i] == '\n') || (buffer[i] == 0)) {
+            // new arg found
+            buffer[i] = 0;
+            if (argno++ == 1) {
+                // it's the uri
+                // parse the uri args
+                parseUriArgs (&buffer[start]);
+            }
+            INFO("Found argument \"%s\"", &buffer[start]);
+            args.push_back(&buffer[start]);
+            start = i+1;
+        }
+    }
+
+    // store the uri and the HTTP version
+    msg->uri = args[1];
+    msg->version = args[2];
+
+    //  Find matching request type
+    for (int i=0; i<sizeof(rq_conf)/sizeof(RequestConfig) ; i++) {
+        if (args.at(0) == rq_conf[i].request_string) {
+            msg->request = rq_conf[i].request_type;
+        }
+    }
+
+    return OK;
+}
+
+/* esta rutina no se usa */
+
+int HTTPServer::parseHeader () {
+
+    //  Check if the buffer content is too short to be meaningful
+    if (strlen(buffer) < MIN_LONG) {return ERROR;}
+
+    //  decompose string into a touple of <field name> : <field value>
+    int value_start = 0;
+    int buflen = strlen(buffer)+1;
+    for (int i=0; i<buflen; i++) {
+        if (buffer[i] == ':') {
+            //  touple found
+            buffer[i] = 0;
+            value_start = i+1;
+            msg->headers[buffer] = &buffer[value_start];
+            INFO("Header name=\"%s\" : value=\"%s\".", buffer, &buffer[value_start]);
+            return OK;
+        }
+    }
+
+    ERR("Did not recieve a valid header : \"%s\".", buffer);
+    return ERROR;
+}
+
+int HTTPServer::parseUriArgs (char *uri_buffer) {
+
+    // Check if the buffer content is too short to be meaningful
+    if (strlen(uri_buffer) < MIN_LONG) {return ERROR;}
+
+    int args_start = -1;
+    int value_start = -1;
+    int buflen = strlen(uri_buffer) + 1;
+    char* argname = NULL;
+    char* valuename = NULL;
+    for (int i=0; i<buflen; i++) {
+        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 ?
+                if (uri_buffer[i] == '=') {
+                    //  yes, separate the arg-name
+                    uri_buffer[i] = 0;
+                    argname = &uri_buffer[args_start];
+                    value_start = i+1;
+                    INFO("Argument name %s", argname);
+                }
+            } else { // search for end of value
+                if ((uri_buffer[i] == '&') || (uri_buffer[i] == 0) || (uri_buffer[i] == '\r') || (uri_buffer[i] == '\n')) {
+                    buffer[i] = 0;
+                    valuename = &uri_buffer[value_start];
+                    INFO("Argument value %s", valuename);
+                    msg->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_;
+
+    switch (msg->request) {
+      case HTTP_RT_GET:
+        INFO("Dispatching GET Request.");
+        err_ = handleGetRequest();
+        break;
+      case HTTP_RT_POST:
+        INFO("Dispatching POST request.");
+        err_ = handlePostRequest();
+        break;
+      default:
+        INFO("Error in handleRequest, unhandled request type.");
+        err_ = 501;      // HTTP_NotImplemented
+        break;
+    }
+
+    //  if any of these functions returns a negative number, call the error handler
+    if (err_ > 0) {
+        handleError (err_);
+    }
+
+}
+
+int HTTPServer::handleGetRequest() {
+
+    INFO("Handling Get Request.");
+
+    int retval = OK;     //success
+
+    // maping to root path
+    std::string reqPath = path + msg->uri.substr(1);
+
+    //  Check if we received a directory with the local path
+    if (reqPath.substr(reqPath.length()-1, 1) == "/") {
+        //  yes, we shall append the default page name
+        reqPath += "index.htm";
+    }
+
+    INFO("Mapping \"%s\" to \"%s\"", msg->uri.c_str(), reqPath.c_str());
+
+    FILE *fp = fopen(reqPath.c_str(), "r");
+    if (fp != NULL) {
+
+        char * pBuffer = NULL;
+        int sz = 8192;                         // fixme harcode
+        while( pBuffer == NULL) {
+            sz /= 2;
+            pBuffer = (char*)malloc(sz);
+            if (sz < 128)                      // fixme harcode
+                error ("OutOfMemory");
+        }
+
+        //  File was found and can be returned
+
+        //  first determine the size
+        fseek(fp, 0, SEEK_END);
+        long size = ftell(fp);
+        fseek(fp, 0, SEEK_SET);
+
+        startResponse (200, size);                  // response: 200
+        while (!feof(fp) && !ferror(fp)) {
+            int cnt = fread (pBuffer, 1, sz , fp);
+            if (cnt < 0)
+                cnt = 0;
+            processResponse (cnt, pBuffer);
+        }
+
+        INFO("Ending Response !");
+
+        free (pBuffer);
+        fclose (fp);
+
+    } else {
+        retval = 404;
+        ERR("Requested file was not found !");
+    }
+
+    return retval;
+
+}
+
+int HTTPServer::handlePostRequest() {
+
+    return 404;
+}
+
+static const char hdrStandard[] = "DNT: 1\r\n"
+                            "MaxAge: 0\r\n"
+                            "Connection: Keep-Alive\r\n"
+                            "Content-Type: text/html\r\n"
+                            "Server: mbed embedded\r\n"
+                            "Accessible: 1\r\n"
+                            "\r\n";
+
+void HTTPServer::startResponse (int returnCode, long nLen) {
+
+    INFO("Starting response (%ld bytes in total)!", nLen);
+
+    sprintf (buffer, "HTTP/1.1 %d OK\r\n", returnCode);
+    cliente->send(buffer, strlen(buffer));
+    sprintf (buffer, "Content-Length: %ld\r\n", nLen);    //  Add 2 chars for the terminating CR+LF
+    cliente->send(buffer, strlen(buffer));
+    INFO("Sending standard headers !");
+    cliente->send_all((char*)hdrStandard, strlen(hdrStandard));
+
+    INFO("Proceeding !");
+    //  other content must be sent using the 'processResponse' function
+}
+
+void HTTPServer::processResponse (int nLen, char* body) {
+
+    INFO("Processing Response (%d bytes)!\n", nLen);
+    cliente->send_all (body, nLen);
+
+}
+
+static const char* errorPage = "<HTML><HEAD><META content=\"text/html\" http-equiv=Content-Type></HEAD><BODY><h1>Error</h1><P>HTTPServer Error<P></BODY></HTML>\r\n\r\n";
+
+void HTTPServer::handleError (int errorCode) {
+
+    INFO("Handling error !");
+
+    sprintf (buffer,"HTTP/1.1 %d Error\r\n", errorCode);
+    cliente->send (buffer, strlen(buffer));
+    sprintf (buffer, "Content-Length: %ld\r\n", strlen(errorPage));
+    cliente->send (buffer, strlen(buffer));
+    sprintf(buffer, "Content-Type: text/html\r\nServer: mbed embedded\r\n\r\n");
+    cliente->send(buffer, strlen(buffer));
+    cliente->send_all((char*)errorPage, strlen(errorPage));
+    cliente->send("\r\n", 3);
+
+    INFO("Done !");
+
+}