My fork of the HTTPServer (working)
Diff: HTTPServer/HTTPServer.h
- Revision:
- 0:7a64fbb4069d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HTTPServer/HTTPServer.h Mon Aug 06 09:23:14 2012 +0000 @@ -0,0 +1,348 @@ +#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; + + u8_t emptypolls; +}; + +/** + * The HTTPServer class. + * It representates the top class of an HTTPServer. + */ +class HTTPServer : public TCPListener { + public: + /** + * Create a new server instance by default on port 80. + */ + HTTPServer(u16_t = 80); + virtual ~HTTPServer() { + fields.clear(); + _handler.clear(); + } + + /** + * 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); + } + + /** + * 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)); + } + + /** + * A short lookup if the headerfield is registerd. + */ + virtual bool isField(unsigned long h) const { + return fields.find(h) != fields.end(); + } + + /** + * 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(); + } + 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; + friend HTTPConnection; +}; + +}; + +#endif /* HTTP_H */