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:
Fri Jul 26 22:05:19 2013 +0000
Child:
1:f0c641cd9bad
Commit message:
HTTP Server ; Copyright (c) 2013 Pablo Gindel (palmer@pablogindel.com); Based on original code by Henry Leinen.

Changed in this revision

EthernetInterface.lib Show annotated file Show diff for this revision Revisions of this file
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
debug.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed-rtos.lib Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/EthernetInterface.lib	Fri Jul 26 22:05:19 2013 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/EthernetInterface/#40640efbfcae
--- /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 !");
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HTTPServer.h	Fri Jul 26 22:05:19 2013 +0000
@@ -0,0 +1,91 @@
+/*
+Copyright (c) 2013 Pablo Gindel (palmer@pablogindel.com)
+Based on original code by Henry Leinen (henry[dot]leinen [at] online [dot] de)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+
+#ifndef __HTTPSERVER_H__
+#define __HTTPSERVER_H__
+
+#include "mbed.h"
+#include "EthernetInterface.h"
+#include <vector>
+#include <string>
+#include <map>
+
+enum RequestType {
+     HTTP_RT_GET,        /*!< GET request */
+     HTTP_RT_POST,       /*!< POST request */
+};
+
+/** HTTPMessage contains all the details of the request received by external HTTP client. */
+ struct HTTPMsg {
+     // Specifies the request type received
+     RequestType request;
+     // The uri associated with the request.
+     std::string uri;
+     // 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;
+     // Map of arguments that came with the uri string
+     std::map<std::string, std::string> args;
+};
+
+struct RequestConfig {
+     const char* request_string;
+     RequestType request_type;
+};
+
+#define BUFFER_SIZE  256
+
+class HTTPServer {
+
+private:
+    TCPSocketServer socketServer;
+    TCPSocketConnection *cliente;
+    HTTPMsg *msg;
+    std::string path;
+    char buffer [BUFFER_SIZE];
+    int pollConnection ();
+    int receiveLine ();
+    int parse ();
+    int parseHeader ();
+    int parseUriArgs (char *uri_buffer);
+    void handleRequest ();
+    int handleGetRequest();
+    int handlePostRequest();
+    void startResponse (int returnCode, long nLen);
+    void processResponse (int nLen, char* body);
+    void handleError (int errorCode);
+
+public:
+        /** Constructor for HTTPServer objects.  */
+        HTTPServer (int port, const char* _path);
+
+        /** Destructor for HTTPServer objects.  */
+        ~HTTPServer();
+
+        int poll();
+};
+
+
+#endif //__HTTPSERVER_H__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debug.h	Fri Jul 26 22:05:19 2013 +0000
@@ -0,0 +1,17 @@
+
+#ifndef __DEBUG_H__
+#define __DEBUG_H__
+
+
+#ifdef DEBUG
+#define INFO(x, ...) std::printf("[INFO: %s:%d]" x "\r\n", __FILE__, __LINE__, ##__VA_ARGS__);
+#define WARN(x, ...) std::printf("[WARN: %s:%d]" x "\r\n", __FILE__, __LINE__, ##__VA_ARGS__);
+#define ERR(x, ...) std::printf("[ERR: %s:%d]" x "\r\n", __FILE__, __LINE__, ##__VA_ARGS__);
+#else
+#define INFO(x, ...)
+#define WARN(x, ...)
+#define ERR(x, ...)
+#endif
+
+
+#endif //   __DEBUG_H__#ifndef __DEBUG_H__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Fri Jul 26 22:05:19 2013 +0000
@@ -0,0 +1,50 @@
+
+#include "mbed.h"
+#include "EthernetInterface.h"
+#include "HTTPServer.h"
+
+LocalFileSystem local("local");
+
+DigitalOut led1(LED1);
+
+void http_thread (void const* arg);                   ///////
+
+int main() {
+   
+    EthernetInterface eth;
+    eth.init(); //Use DHCP
+    eth.connect();
+    printf("IP Address is %s\n", eth.getIPAddress());
+    
+    Timer onesec;
+    onesec.start();
+
+    Thread httpsvr( &http_thread );
+
+    while (true) {
+
+        if (onesec.read() > 1) {
+            onesec.reset();
+            led1 = 1-led1;
+        }
+        
+    }
+    
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+//                                            HTTP THREAD                                           //
+//////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void http_thread (void const* arg) {
+
+    HTTPServer svr (80, "/local/");    // esto incluye el init
+
+    // osThreadSetPriority( Thread::gettid() ,  osPriorityBelowNormal );
+
+    while (1) {
+
+        svr.poll();
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-rtos.lib	Fri Jul 26 22:05:19 2013 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed-rtos/#58b30ac3f00e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Fri Jul 26 22:05:19 2013 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/b3110cd2dd17
\ No newline at end of file