Single instance HTTP Server using new Ethernet Interface with bug fix for URL arguments
Fork of HTTPServer by
Revision 0:7a2421e63e74, committed 2013-05-26
- Comitter:
- leihen
- Date:
- Sun May 26 20:13:28 2013 +0000
- Child:
- 1:6b7472d5e9ee
- Commit message:
- First draft, which does not actually handle a request.
; Framework is working though.
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HTTPConnection.cpp Sun May 26 20:13:28 2013 +0000 @@ -0,0 +1,219 @@ +/* HTTPConnection.cpp */ + +#include "mbed.h" +#include "HTTPConnection.h" + +#include <vector> +using std::vector; + +using std::string; + +#if (1 && !defined(TARGET_LPC11U24)) +#define INFO(x, ...) std::printf("[HttpConnection : INFO]"x"\r\n", ##__VA_ARGS__); +#define WARN(x, ...) std::printf("[HttpConnection : WARN]"x"\r\n", ##__VA_ARGS__); +#define ERR(x, ...) std::printf("[HttpConnection : ERR]"x"\r\n", ##__VA_ARGS__); +#else +#define INFO(x, ...) +#define WARN(x, ...) +#define ERR(x, ...) +#endif + + + + +HTTPConnection::HTTPConnection() +{ +} + + +HTTPConnection::~HTTPConnection() +{ + close(); +} + +void HTTPConnection::close() +{ + m_Msg.headers.clear(); +} + +int HTTPConnection::poll() +{ + static char buffer[256] = {}; + static char echoHeader[256] = {}; + + + int rcvd= 0; + INFO("[HTTPConnection]Waiting for new data in connection"); + // Try receiving request line + rcvd = receiveLine(buffer, 255, 3000); + if (rcvd == -1) { + // 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."); + close(); + return -1; + } + + // The Request has not yet been received so try it + rcvd = parse(buffer); + if (rcvd == -1) { + // Invalid content received, so close the connection + INFO("Invalid message received, so sending negative response and closing connection !"); + sprintf(echoHeader,"HTTP/1.1 400 NOK\n\rContent-Length: %d\n\rContent-Type: text\n\rConnection: Close\n\r\n\r",strlen(buffer)); + m_Tcp.set_blocking(true, 1500); + m_Tcp.send(echoHeader,strlen(echoHeader)); + m_Tcp.send(buffer,strlen(buffer)); + close(); + rcvd = -1; + return -1; + } + // The request has been received, try receive the body + while(rcvd > 0) { + rcvd = receiveLine((char*)buffer, 255, 3000); + // First check if we received an empty line. This would indicate the end of the message or message body. + if (rcvd < 0) { + // there was an empty line, so we can start with performing the request + INFO("Request Header was received completely. Performing request."); + rcvd = 0; + break; + } + else { + // add message body + if (parseHeader(buffer) == 0) { + } + else { + WARN("Invalid message header received !"); + } + } + } + if (rcvd == 0) { + sprintf(echoHeader,"HTTP/1.1 200 OK\n\rContent-Length: %d\n\rContent-Type: text\n\rConnection: Close\n\r\n\r",strlen(buffer)); + m_Tcp.set_blocking(true); + m_Tcp.send_all(echoHeader,strlen(echoHeader)); + m_Tcp.send_all(buffer,strlen(buffer)); + + /// INSERT PRCESSING OF REQUESST HERE + /// END OF PROCESSING REQUEST + + // Do not close the connection, it may be reused + } + INFO("Leaving poll function!"); + return rcvd; +} + +int HTTPConnection::receiveLine(char* szLine, int nMaxLen, int nTimeout, char cLineTerm) +{ + if ((szLine == NULL) || (nMaxLen == 0)) + return -1; + + m_Tcp.set_blocking(false); + + Timer tm; + int i; + + // Try to receive up to the max number of characters + for (i = 0 ; i < nMaxLen-1 ; i++) { + int c; + c = m_Tcp.receive_all( szLine + 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() > nTimeout) { + // Operation timed out + INFO("Timeout occured in function 'receiveLine'."); + return -1; + } + } + + // Check if line terminating character was received + if (szLine[i] == cLineTerm) + break; + } + // Terminate with \0 + szLine[i] = 0; + + // Trim for '\r' linefeed at the end + if( (i >0) && (szLine[i-1] == '\r')) { + i--; + szLine[i] = 0; + } + INFO("receiveLine : \"%s\".", szLine); + + // return number of characters received in the line or return -2 if an empty line was received + if ((i == 0) || ((i==1) &&(szLine[0] == '\r'))) + { + // empty line received, so return -2 + return -2; + } + return i; +} + +int HTTPConnection::parse(const char* buffer) +{ + if ((buffer == NULL) || (strlen(buffer) < 4)) { + ERR("Buffer content is invalid or too short."); + return -1; + } + + vector<std::string> args; + args.clear(); + + // decompose string into a list of arguments + char s = 0; // current starting char + static char buff[255] = {}; + for (int i = 0 ; i < strlen(buffer)+1 ; i++) { + if ((buffer[i] == ' ') || (buffer[i] == '\n') || (buffer[i] == 0)) { + // new arg found + strncpy(buff, &buffer[s], i-s); + buff[i-s] = 0; + INFO("Found argument \"%s\"", buff); + args.push_back(std::string(buff)); + s = i+1; + } + } + + if (args.at(0) == "GET") { + m_Msg.request = HTTP_RT_GET; + m_Msg.uri = args[1]; + m_Msg.version = args[2]; + } + else { + if (args.at(0) == "POST") { + m_Msg.request = HTTP_RT_GET; + m_Msg.uri = args[1]; + m_Msg.version = args[2]; + } + else { + INFO("unhandled message."); + } + } + args.clear(); + + return 1; +} + + +int HTTPConnection::parseHeader(const char *buffer) +{ + if ((strlen(buffer) <3) || (buffer == NULL)) + return -1; + + // decompose string into a touple of <field name> : <field value> + static char fieldname[256] = {}; + static char fieldvalue[256] = {}; + for (int i = 0 ; i < strlen(buffer)+1 ; i++) { + if (buffer[i] == ':') { + // touple found + strncpy(fieldname, buffer, i); + fieldname[i] = 0; + strcpy(fieldvalue, &buffer[i+1]); + +// m_Msg.headers[fieldname] = fieldvalue; + + INFO("Header name=\"%s\" : value=\"%s\".", fieldname, fieldvalue); + return 0; + } + } + + ERR("Did not recieve a valid header : \"%s\".", buffer); + return -1; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HTTPConnection.h Sun May 26 20:13:28 2013 +0000 @@ -0,0 +1,69 @@ +/* HTTPConnection.h */ +#ifndef __HTTPConnection_H__ +#define __HTTPConnection_H__ + +#include "mbed.h" +#include "TCPSocketConnection.h" + +#include <string> +#include <map> + +enum HTTPRequestType +{ + HTTP_RT_GET, + HTTP_RT_POST, + HTTP_RT_PUT, + HTTP_RT_OPTIONS, + HTTP_RT_HEAD, + HTTP_RT_DELETE, + HTTP_RT_TRACE, + HTTP_RT_CONNECT +}; + +struct HTTPMessage +{ + HTTPRequestType request; + std::string uri; + std::string version; + std::map<string, string> headers; +}; + +/** class HTTPConnection, encapsulates one connection being made throught the HTTPServer + * + */ +class HTTPConnection { + public: + /** public constructor + * + */ + HTTPConnection (); + ~HTTPConnection(); + + /** function to close this connection. To be called from internally. + */ + void close(); + + /** query if this connection is closed and can be deleted. + @returns true if connection is closed. + */ + bool is_closed(); + + /** + Polling function + @returns -1 if connection is not required anymore. Can happen if a fault occured or if the connection is not needed anymore. + */ + int poll(); + + protected: + + TCPSocketConnection m_Tcp; + + HTTPMessage m_Msg; + int parse(const char *buffer); + int parseHeader(const char *buffer); + int receiveHeaders(const char* buffer, int nBuffSize); + int receiveLine(char* szLine, int nMaxLen, int nTimeout = -1, char szLineTerm = '\n'); + +}; + +#endif // __HTTPConnection_H__ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HTTPServer.cpp Sun May 26 20:13:28 2013 +0000 @@ -0,0 +1,118 @@ +#include "mbed.h" +#include "HTTPServer.h" + +DigitalOut led1(LED1); +DigitalOut led2(LED2); +DigitalOut led3(LED3); +DigitalOut led4(LED4); + +#if (1 && !defined(TARGET_LPC11U24)) +#define INFO(x, ...) if (m_pDbg) m_pDbg->printf("[HttpServer : DBG]"x"\r\n", ##__VA_ARGS__); else printf("[HttpServer : DBG]"x"\r\n", ##__VA_ARGS__); +#define WARN(x, ...) if (m_pDbg) m_pDbg->printf("[HttpServer : WARN]"x"\r\n", ##__VA_ARGS__); else printf("[HttpServer : DBG]"x"\r\n", ##__VA_ARGS__); +#define ERR(x, ...) if (m_pDbg) m_pDbg->printf("[HttpServer : ERR]"x"\r\n", ##__VA_ARGS__); else printf("[HttpServer : DBG]"x"\r\n", ##__VA_ARGS__); +#else +#define INFO(x, ...) +#define WARN(x, ...) +#define ERR(x, ...) +#endif + + +HTTPServer::HTTPServer(Serial* pDbg) +{ + m_pDbg = pDbg; + m_pSvr = NULL; + m_bServerListening = false; +} + +HTTPServer::~HTTPServer() +{ + if (m_pSvr) { + delete m_pSvr; + m_pSvr = NULL; + m_bServerListening = false; + } +} + +int HTTPServer::start(int port) +{ + // check if the start member was called already once + if (m_pSvr != NULL) { + ERR("start function was already called, server is already in listening state."); + return -1; + } + + m_bServerListening = false; + + // Create a new server object + m_pSvr = new TCPSocketServer(); + + // Bind the local server to the given port + if (m_pSvr->bind(port) < 0) { + ERR("Failed to bind to port %d\n", port); + return -1; + } + else { + INFO("Binding succeeded !\n"); + } + + // Listen to a maximum of 10 concurrent connections + if (m_pSvr->listen(1) < 0) { + ERR("Faild to listen !\n"); + delete m_pSvr; + m_pSvr = NULL; + return -1; + } + else { + INFO("Listening\n"); + m_bServerListening = true; + } + + // set into non blocking operation + m_pSvr->set_blocking(false, 100); + + return 0; +} + + +int HTTPServer::poll() +{ + INFO("Listening for new connection requests."); + + // This thread basically checks if there is a new incoming connection. + // If so , a new HTTPConnection is created and the connection thread is started. + TCPSocketConnection Clnt; + + led4 = 1; // Indicate we are waiting for a new connection + if (m_pSvr->accept(Clnt) < 0) { + // an error occured + ERR("There was an error, Accept returned with an error. Probably the connection to the router was lost. Shutting down server"); + led2 = 0; + m_bServerListening = false; + m_pSvr->close(); + delete m_pSvr; + m_pSvr = NULL; + led4 = 0; + led3 = 1; // ERROR + led2 = 0; + led1 = 0; + return -1; + } + else { + led4 = 0; + // a new connection was received + INFO("Client (IP=%s) is connected !\n", Clnt.get_address()); + // Start the main connection thread + led3 = 1; + led2 = 1; + HTTPConnection con; + int c = con.poll(); + if (c == 0) { + } + led2 = 0; + led3 = 0; + } + + + INFO("Leaving polling thread"); + return 0; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HTTPServer.h Sun May 26 20:13:28 2013 +0000 @@ -0,0 +1,68 @@ +/* HTTPServer.cpp */ +#ifndef __HTTPSERVER_H__ +#define __HTTPSERVER_H__ +#include "mbed.h" +#include "HTTPConnection.h" + +#include <TCPSocketConnection.h> +#include <TCPSocketServer.h> + +/** Class HTTPServer for WiFly Interface Library + * + */ + class HTTPServer + { + + public: + HTTPServer(Serial* pDbg = NULL); + ~HTTPServer(); +/* + struct handlersComp //Used to order handlers in the right way + { + bool operator() (const string& handler1, const string& handler2) const + { + //The first handler is longer than the second one + if (handler1.length() > handler2.length()) + return true; //Returns true if handler1 is to appear before handler2 + else if (handler1.length() < handler2.length()) + return false; + else //To avoid the == case, sort now by address + return ((&handler1)>(&handler2)); + } + }; + */ + ///Adds a handler + /** + Appends a handler to the handlers list + @param T : class which will be instanciated to serve these requests + @param path : requests starting with this path will be served using this handler + */ +// template<typename T> +// void addHandler(const char* path) //Template decl in header +// { m_lpHandlers[path] = &T::inst; } + + ///Starts listening + /** + Binds server to a specific port and starts listening. This member prepares the internal variables and the server socket + and terminates after successfull initialization + @param port : port on which to listen for incoming connections + @returns : -1 if an unrecoverable error occured, or 0 if everything was ok. + */ + int start(int port = 80); + + /** + Performs the regular polling of the server component. Needs to be called cyclically. + The function will internally check whether new connections are requested by a client and will also poll all existing client connections. + */ + int poll(); + +private: + + TCPSocketServer* m_pSvr; + bool m_bServerListening; + + Serial* m_pDbg; + + }; + + #endif //__HTTPSERVER_H__ \ No newline at end of file