Port of LwIP performed by Ralf in 2010. Not recommended for use with recent mbed libraries, but good demos of raw LwIP possible

Dependents:   LwIP_raw_API_serverExample tiny-dtls

Revision:
0:0791c1fece8e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HTTPServer/HTTPServer.h	Tue Sep 18 14:41:24 2012 +0000
@@ -0,0 +1,406 @@
+#ifndef HTTPSERVER_H
+#define HTTPSERVER_H
+
+#include "TCPConnection.h"
+#include "TCPListener.h"
+#include "NetServer.h"
+
+#include <map>
+#include <set>
+#include <list>
+
+#define HTTP_MAX_EMPTYPOLLS 100
+#define GET 4
+#define POST 5
+
+//extern unsigned int gconnections;
+
+using namespace std;
+
+namespace mbed {
+
+class HTTPServer;
+class HTTPHandler;
+class HTTPConnection;
+
+/**
+ * A simple HASH function to reduce the size of stored Header Fields
+ * TODO: Make the Hash case insensetive.
+ */
+unsigned int hash(unsigned char *str);
+
+/**
+ * The Status of an HTTP Request
+ * Nedded for HTTPHandler subclasses to define there reults in the HTTPHandler:init Method.
+ */
+enum HTTPStatus {
+  HTTP_OK                          = 200,
+  HTTP_BadRequest                  = 400,
+  HTTP_Unauthorized                = 401,
+  HTTP_Forbidden                   = 403,
+  HTTP_NotFound                    = 404,
+  HTTP_MethodNotAllowed            = 405,
+  HTTP_InternalServerError         = 500,
+  HTTP_NotImplemented              = 501,
+};
+
+/**
+ * The result of a chunk of data used for the HTTPHandler Methodes data and send
+ */
+enum HTTPHandle {
+  /** Execution Succeeded but more data expected. */
+  HTTP_Success                     = 600,
+  
+  /** Running out of memory waiting for memory */
+  HTTP_SenderMemory                = 601,
+
+  
+  /** Execution Succeeded and no more data expected. */
+  HTTP_SuccessEnded                = 700,
+  
+  /** Execution failed. Close conection*/
+  HTTP_Failed                      = 701,
+  
+  /** This module will deliver the data. */
+  HTTP_Deliver                     = 800,
+  
+  /** This module has add header fields to the request. */
+  HTTP_AddFields                   = 801,
+};
+
+/**
+ * A parent object for a data storage container for all HTTPHandler objects.
+ */
+class HTTPData {
+  public:
+    HTTPData() {}
+    virtual ~HTTPData() {}
+};
+
+/**
+ * A HTTPHandler will serve the requested data if there is an object of a 
+ * child class from HTTPHandler which is registert to an matching prefix.
+ * To see how to implement your own HTTPHandler classes have a look at 
+ * HTTPRPC HTTPStaticPage and HTTPFileSystemHandler.
+ */
+class HTTPHandler {
+  public:
+    HTTPHandler(const char *prefix) : _prefix(prefix) {};
+    virtual ~HTTPHandler() {
+      delete _prefix;
+    };
+
+  protected:
+    /**
+     * Register needed header fields by the HTTPServer.
+     * Because of memory size the server will throw away all request header fields which are not registert.
+     * Register the fields you need in your implementation of this method.
+     */
+    virtual void reg(HTTPServer *) {};
+    
+    /**
+     * This Method returns if you will deliver the requested page or not.
+     * It will only executed if the prefix is matched by the URL. 
+     * If you want to add something to the headerfiles use this method and return HTTP_AddFields. See HTTPFields
+     * This would be the right method to implement an Auth Handler.
+     */
+    virtual HTTPHandle action(HTTPConnection *) const                    {return HTTP_Deliver;}
+    
+    /**
+     * If action returned HTTP_Deliver. 
+     * This function will be executed and it means your handler will be deliver the requested data.
+     * In this method is the right place to allocate the needed space for your request data and to prepare the sended Header.
+     */
+    virtual HTTPStatus init(HTTPConnection *) const                      {return HTTP_NotFound;}
+    
+    /**
+     * If data from a post request is arrived for an request you accepted this function will be executed with the data.
+     * @param data A pointer to the received data.
+     * @param len The length of the received data.
+     * @return Return an HTTPHandle. For example HTTP_SuccessEnded if you received all your needed data and want to close the conection (normally not the case).
+     */
+    virtual HTTPHandle data(HTTPConnection *, void *data, int len) const {return HTTP_SuccessEnded;}
+    
+    /**
+     * If tere is new space in the sendbuffer this function is executed. You can send maximal Bytes of data.
+     * @return Return an HTTPHandle. For example HTTP_SuccessEnded if you send out all your data and you want to close the connection.
+     */
+    virtual HTTPHandle send(HTTPConnection *, int) const                 {return HTTP_SuccessEnded;}
+    
+    /**
+     * returns the Prefix from the HTTPHandler
+     */
+    const char *getPrefix() const                                        {return _prefix;}
+
+    const char *_prefix;
+    
+  friend HTTPServer;
+  friend HTTPConnection;
+};
+
+/**
+ * For every incomming connection we have a HTTPConnection object which will handle the requests of this connection.
+ */
+class HTTPConnection : public TCPConnection {
+  public:
+    /**
+     * Constructs a new connection object from a server.
+     * It just need the server object to contact the handlers
+     * and the tcp connection pcb.
+     * @param parent The server which created the connection.
+     * @param pcb The pcb object NetServers internal representation of an TCP Connection
+     */
+    HTTPConnection(HTTPServer *parent, struct tcp_pcb *pcb);
+    /**
+     * Default destructor. Simple cleanup.
+     */
+    virtual ~HTTPConnection();
+
+    /**
+     * Get the requested url.
+     * Only set if a request ist received.
+     */
+    char *getURL() const                  { return _request_url; }
+    
+    /**
+     * Gets a string of set fields to send with the answere header.
+     */
+    const char *getHeaderFields() const   { return (_request_headerfields)?_request_headerfields:""; }
+    
+    /**
+     * Gets the length of the anwere in bytes. This is requiered for the HTTP Header.
+     * It should be set over setLength by an HTTPHandler in the init() method.
+     */
+    const int  &getLength() const         { return _request_length; }
+    
+    /**
+     * Gets POST or GET or 0 depends on wether ther is a request and what type is requested.
+     */
+    const char &getType() const           { return _request_type; }
+    
+    /**
+     * Gets a value from a header field of the request header.
+     * But you must have registerd this headerfield by the HTTPServer before.
+     * Use the HTTPHandler::reg() method for the registration of important header fields for your Handler.
+     */
+    const char *getField(char *key) const;
+    
+    /**
+     * For internal usage. Adds an header field value to its hash.
+     * If it was registered You can see the Value with the getField method
+     */
+    void addField(char *key, char *value);
+    
+    /**
+     * Sets the result length for an request shoud be set in an HTTPHandler.init() call.
+     * This Value will be send with the response header before the first chunk of data is send.
+     */
+    void setLength(const int &value)      { _request_length = value; }
+    
+    /**
+     * Set the response header field to a value.
+     * Should be used in the HTTPHandler::init() method.
+     * For example if you want to set caching methods.
+     */
+    void setHeaderFields(char *value)     { _request_headerfields = value; }
+    
+    /** Indicates that if a request is received the header is incomplete until now. */
+    bool request_incomplete;
+    
+    /** If an request is complete HTTPHandler:init will be called and can store here its connection data. */
+    HTTPData *data;
+    
+    /** The handler which handles the current request. Depends on the prefix of the URL. */
+    HTTPHandler *request_handler;
+    
+    /** The status of the request. Will be set as result of HTTPHandler::init. */
+    HTTPStatus request_status;
+
+    /** The HTTTPServer which created this connection. */
+    HTTPServer *parent;
+  private:
+    virtual void err(err_t err);
+    virtual err_t poll();
+    virtual err_t sent(u16_t len);
+    virtual err_t recv(struct pbuf *q, err_t err);
+
+    /** We will not make any DNS requests. */
+    virtual void dnsreply(const char *, struct ip_addr *) {}
+    
+    /** If a request is finished it will be deleted with this method. Simple cleanup. */
+    void deleteRequest();
+
+    /** Call the handler to send the next chunk of data. */
+    void send();
+    
+    /** Call the handler if we received new data. */
+    void store(void *d, struct pbuf *p);
+    
+    /** 
+     * If a request header is not complete we can colect needed header fields. 
+     * This happens in here.
+     */
+    void getFields(struct pbuf **q, char **d);
+
+    char *_request_url;
+    char  _request_type;
+    char *_request_headerfields;
+    map<unsigned int, char *> _request_fields;
+    int _request_length;
+    char *_request_arg_key;
+    char *_request_arg_value;
+    char  _request_arg_state;
+    
+    unsigned int emptypolls; // Last time for timeout
+    unsigned int _timeout_max;
+};
+
+/* Class HTTPServer
+ *  An object of this class is an HTTPServer instance and will anwere requests on a given port.
+ *  It will deliver HTTP pages
+ */
+class HTTPServer : public TCPListener {
+  public:
+
+    /* Constructor: HTTPServer
+     *  Creates an HTTPServer object. You might want to initialise the network server befor.
+     *  If you dont do it it will be happen by the first post or get request you make.
+     *
+     *  To initialize the network server on creation of the HTTPServer object it's possible to parse some arguments:
+     * Variables:
+     *  hostname - A host name for the device. Might take a while to appear in the network, 
+     *             depends on the network infrastructure. Furthermore in most cases you have 
+     *             to add your domainname after the host name to address the device.
+     *             Default is NULL.
+     *  ip  - The device ipaddress or ip_addr_any for dhcp. Default is ip_addr_any
+     *  nm  - The device netmask or ip_addr_any for dhcp. Default is ip_addr_any.
+     *  gw  - The device gateway or ip_addr_any for dhcp. Default is ip_addr_any.
+     *  dns - The device first dns server ip or ip_addr_any for dhcp. Default is ip_addr_any.
+     *
+     * Example:
+     *  > HTTPServer http;         // Simple DHCP, brings up the TCP/IP stack on bind(). Default prot is port 80.
+     *  > HTTPServer http(8080);   // Port is here 8080.
+     *  
+     *  > HTTPServer http("worf"); // Brings up the device with DHCP and sets the host name "worf"
+     *  >                          // The device will be available under worf.<your local domain> 
+     *  >                          // for example worf.1-2-3-4.dynamic.sky.com
+     *
+     *  > HTTPServer http("wolf",              // Brings up the device with static IP address and domain name.
+     *  >                 IPv4(192,168,0,44),  // IPv4 is a helper function which allows to rtype ipaddresses direct
+     *  >                 IPv4(255,255,255,0), // as numbers in C++.
+     *  >                 IPv4(192,168,0,1),   // the device address is set to 192.168.0.44, netmask 255.255.255.0
+     *  >                 IPv4(192,168,0,1)    // default gateway is 192.168.0.1 and dns to 192.168.0.1 as well.
+     *  >                 8080);               // And port is on 8080. Default port is 80.
+     */
+
+    HTTPServer(const char *hostname, struct ip_addr ip = ip_addr_any, struct ip_addr nm = ip_addr_any, struct ip_addr gw = ip_addr_any, struct ip_addr dns = ip_addr_any, unsigned short port = 80);
+    HTTPServer(unsigned short port = 80);
+
+    /* Destructor: ~HTTPServer
+     *  Destroys the HTTPServer and all open connections.
+     */
+    virtual ~HTTPServer() {
+      fields.clear();
+      _handler.clear();
+    }
+
+    /* Function: addHandler
+     *  Add a new content handler to handle requests.
+     *  Content handler are URL prefix specific.
+     *  Have a look at HTTPRPC and HTTPFileSystemHandler for examples.
+     */
+    virtual void addHandler(HTTPHandler *handler) {
+      _handler.push_back(handler);
+      handler->reg(this);
+    }
+    
+    /* Function registerField
+     *  Register needed header fields to filter from a request header.
+     *  Should be called from HTTPHandler::reg()
+     */
+    virtual void registerField(char *name) {
+      fields.insert(hash((unsigned char *)name));
+    }
+
+    /* Function isField
+     *  A short lookup if the headerfield is registerd.
+     */
+    virtual bool isField(unsigned long h) const {
+      return fields.find(h) != fields.end();
+    }
+    
+    /* Function: poll 
+     *   You have to call this method at least every 250ms to let the http server run.
+     *   But I would recomend to call this function as fast as possible.
+     *   This function is directly coupled to the answere time of your HTTPServer instance.
+     */
+    inline static void poll() {
+      NetServer::poll();
+    }
+
+    /* Function: timeout
+     *  Sets the timout for a HTTP request. 
+     *  The timout is the time wich is allowed to spent between two incomming TCP packets. 
+     *  If the time is passed the connection will be closed.
+     */
+    void timeout(int value) {
+      _timeout_max = value;
+    }
+
+    /* Function timeout
+     *  Returns the timout to use it in HTTPHandlers and HTTPConnections
+     */
+    int timeout() {
+      return _timeout_max;
+    }
+  private:
+    /**
+     * Pick up the right handler to deliver the response.
+     */
+    virtual HTTPHandler *handle(HTTPConnection *con) const {
+      for(list<HTTPHandler *>::const_iterator iter = _handler.begin();
+          iter != _handler.end(); iter++) {
+        if(strncmp((*iter)->getPrefix(), con->getURL(), strlen((*iter)->getPrefix()))==0) {
+          HTTPHandler *handler = *iter;
+          if(handler->action(con)==HTTP_Deliver) {
+            return *iter;
+          }
+        }
+      }
+      return NULL;
+    }
+
+    /**
+     * Accept an incomming connection and fork a HTTPConnection if we have enought memory.
+     */
+    virtual err_t accept(struct tcp_pcb *pcb, err_t err) {
+      LWIP_UNUSED_ARG(err);
+      HTTPConnection *con = new HTTPConnection(this, pcb);
+//      printf("New Connection opend. Now are %u connections open\n", ++gconnections);
+      if(con == NULL) {
+        printf("http_accept: Out of memory\n");
+        return ERR_MEM;
+      }
+      con->set_poll_interval(1);
+      tcp_setprio(pcb, TCP_PRIO_MIN);
+      return ERR_OK;
+    }
+
+    /** The registerd request header fields */
+    set<unsigned int> fields;
+    
+    /** A List of all registered handler. */
+    list<HTTPHandler *> _handler;
+
+    int _timeout_max;
+
+  friend HTTPConnection;
+};
+
+};
+
+#include "HTTPRPC.h"
+#include "HTTPFS.h"
+#include "HTTPFields.h"
+
+#endif /* HTTP_H */