Working Multithreaded HTTP Server using WiFly module. Currently only supporting GET method.
Dependencies: WiFlyHTTPServer WiflyInterface mbed-rpc mbed-rtos mbed
RTOS Wifly HTTP Server
This sample application demonstrates how the HTTP Server can be used in a multithreaded (RTOS) Environment. However currently only the GET method is supported.
Revision 1:40eadac4750b, committed 2013-06-26
- Comitter:
- leihen
- Date:
- Wed Jun 26 22:41:58 2013 +0000
- Parent:
- 0:9c6ebc97c758
- Commit message:
- UPdated the HTTPServer Library
Changed in this revision
--- a/HttpServer.cpp Wed Jun 26 21:13:55 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,477 +0,0 @@ -#include "mbed.h" -#include "HttpServer.h" - -#define DEBUG -#include "debug.h" - - -#define EVENT_DATA_READY 0x05 - -DigitalOut ledRX(LED4); - - -typedef struct { - char c; -} message_t; - -Queue<char, 256> m_queue; - -typedef struct { - const char* method; - msg_t type; -} methodType_t; - -const methodType_t supportedOps[] = { - { "GET", msg_get }, - { "POST", msg_post }, - { "PUT", msg_put }, - { "HEAD", msg_head}, - { "CONNECT", msg_connect}, - { "DELETE", msg_delete}, - { "TRACE", msg_trace}, - { "OPTIONS", msg_options} -}; - - -Queue<request_msg_t, 5> m_requestQueue; // Do not allow more than 5 concurrent requests -MemoryPool<request_msg_t, 5> m_requestPool; - -map<string, string> messageHeaders; - -map<string, HTTPRequestHandler* (*)(const char*, const char*, HTTPConnection::HTTPMessage&), HttpServer::handlersComp> HttpServer::m_lpHandlers; - - - -/* Constructor will create and initialize all objects excep the threads */ -HttpServer::HttpServer(PinName tx, PinName rx, PinName rst, PinName tcp_status, const char * ssid, const char * phrase, Security sec, Wifly::WiflyBaudrate_t baud) - : Wifly(tx, rx, rst, tcp_status, ssid, phrase, sec, baud), m_listener(NULL), m_worker(NULL) -{ - INFO("Initializing wifly\n"); - // Initialize the wifly wlan device - reset(); - - state.dhcp = true; - INFO("Connecting to network..."); - // Try join the network - while(!join()) { - INFO("Failed to connect. Trying again\n"); - reset(); - } - INFO("connected\n"); -} - -HttpServer::~HttpServer() -{ - if (m_listener) { - m_listener->terminate(); - delete m_listener; - } - if (m_worker) { - m_worker->terminate(); - delete m_worker; - } -} - - -bool HttpServer::start(int port) -{ - // Bind to that port - if (!bind(port)) { - ERR("Failed to bind to port %d\n", port); - return false; - } - - // Start the child threads - m_worker = new Thread(HttpServer::worker_thread, NULL, osPriorityAboveNormal, DEFAULT_STACK_SIZE*4); - if (m_worker == NULL) { - ERR("Failed to start server thread !\n"); - return false; - } - - m_listener = new Thread(&HttpServer::listen_thread, NULL, osPriorityAboveNormal, DEFAULT_STACK_SIZE*2); - if (m_listener == NULL) { - ERR("Failed to start listener thread !\n"); - m_worker->terminate(); - delete m_worker; - m_worker = NULL; - return false; - } - - return true; -} - - - - - -bool HttpServer::bind(int port) -{ - char cmd[20]; - - // set TCP protocol - setProtocol(TCP); - - // set local port - sprintf(cmd, "set i l %d\r", port); - if (!sendCommand(cmd, "AOK")) - return false; - - // save - if (!sendCommand("save\r", "Stor")) - return false; - - // reboot - reboot(); - - // connect the network - if (isDHCP()) { - if (!sendCommand("join\r", "DHCP=ON", NULL, 10000)) - return false; - } else { - if (!sendCommand("join\r", "Associated", NULL, 10000)) - return false; - } - - // exit - exit(); - - Thread::wait(200); - flush(); - - return true; -} - -DigitalOut Led2(LED2); - -void HttpServer::handler_rx(void) -{ - static char sequence = 0; - //read characters - while (wifi.readable()) { - char c = LPC_UART3->RBR; - ledRX = !ledRX; - switch(sequence) { - case 0 : if (c == 'G') sequence = 1; break; - case 1 : if (c == 'E') sequence = 2; break; - case 2 : if (c == 'T') sequence = 0; Led2 = !Led2;break; - default: break; - } - m_queue.put((char*)(int)c); - } -} - - -void HttpServer::attach_rx(bool callback) -{ - if (!callback) - wifi.attach(NULL); - else - wifi.attach(this, &HttpServer::handler_rx); -} - - -bool HttpServer::join() -{ - return Wifly::join(); -} - -int HttpServer::send(const char * str, int len, const char * ACK, char * res, int timeout) -{ - return Wifly::send(str, len, ACK, res, timeout); -} - -request_msg_t* HttpServer::checkMessageReceived(char *data) -{ - INFO("Checking for new HTTP request !\n"); - char *req = data; - char *uri = NULL; - char *ver = NULL; - while( *data ) { - if (*data == ' ') { - *data = 0; - if (uri == NULL) { - uri = data+1; - } else { - ver = data+1; - break; - } - } - data++; - } - - INFO("Detected : %s, %s, %s\n", req, uri, ver); - - if ((req != NULL) && (uri != NULL) && (ver != NULL) ) { - for (int i = 0 ; i < sizeof(supportedOps) / sizeof(methodType_t) ; i++) { - if (strcmp(supportedOps[i].method, req) == 0) { - // found the request - INFO("Request valid !!!\n"); - request_msg_t* pmsg = m_requestPool.alloc(); - pmsg->requestType = supportedOps[i].type; - strncpy(pmsg->requestUri, uri, 255); - return pmsg; - } - } - } - - INFO("Invalid request \"%s\"\n", req); - return NULL; -} - -void HttpServer::processMessageHeader(char* headerLine, char **fieldname, char **fieldvalue) -{ - *fieldname = headerLine; - *fieldvalue = NULL; - - while( *headerLine ) { - if (*headerLine == ':') { - *headerLine++ = 0; - while(*headerLine == ' ') headerLine++; - *fieldvalue = headerLine; - return; - } - headerLine++; - } - return ; -} - - -void HttpServer::listenForRequests() -{ - static char data[256]; - static int curPos = 0; - int CRLF = 0; - int m_openConnections = 0; - - request_msg_t *pMsg = NULL; - INFO("Listener running\n"); - bool asteriskReceivedOnce = false; - while(1) { - osEvent evt = m_queue.get(); - if (evt.status == osEventMessage) { - char c; - c = (char)(int)evt.value.p; - if ((c!='\n') && (c!='\r')) { - data[curPos++] = c; - data[curPos] = 0; - } - if (pMsg != NULL) { // request was detected and will further be processed completely - // check for CRLF - if (c == '\n') { - CRLF++; - INFO("<CR>(%d)", CRLF); - if (CRLF == 2) { // all message headers received, so send message and be ready for new one - CRLF = 0; - // SPAWN MESSAGE - INFO("REQUEST COMPLETE --> Handing over to worker thread !\n\n\n\n"); - m_requestQueue.put(pMsg); - data[0] = 0; - curPos = 0; - asteriskReceivedOnce = false; - pMsg = NULL; - } else { // must be a new header -// char *name, *value; -// INFO("Processing Header !\"%s\"", data); -/* processMessageHeader(data, &name, &value); - if (strncmp(name, "Content-Length", 14 ) == 0) { - // Data will be sent, be ready to receive - } else { - INFO("HEADER: Name=\"%s\", Value=\"%s\"", name, value); - } -*/ data[0] = 0; - curPos = 0; - } - } else { - if (c != '\r') - CRLF = 0; - else - INFO("<LF>"); - } - } else if (c == '*') { - CRLF = 0; - if (asteriskReceivedOnce) { - // could be an open, close or read command - if (curPos >= 6) { // only need to process if data is large enough - if ( (data[curPos-6] == '*') && (data[curPos-5] == 'O') && (data[curPos-4] == 'P') && (data[curPos-3] == 'E') && (data[curPos-2] == 'N') && (data[curPos-1] == '*')) { - // Add a connection - INFO("New connection opened (%d)...\n", ++m_openConnections); - data[0] = 0; - curPos = 0; - } else if ( (data[curPos-6] == '*') && (data[curPos-5] == 'C') && (data[curPos-4] == 'L') && (data[curPos-3] == 'O') && (data[curPos-2] == 'S') && (data[curPos-1] == '*')) { - // close a connection - INFO("Connection was closed ...(%d)\n", --m_openConnections); - data[0] = 0; - curPos = 0; - } - } - asteriskReceivedOnce = false; - } else { // set the indicator so that next time we'll check for valid connection commands - asteriskReceivedOnce = true; - } - } else { // first make sure that when no asterisk is received the asteriskReceivedOnce flag will be reset on each newline - if (c == '\n') { - if (m_openConnections > 0) { - // Check to see if we received a valid request - pMsg = checkMessageReceived(data); - if (pMsg == NULL) { - // not received valid stuff, so discard - INFO("Unrecognised data received : \"%s\"\n", data); - } else { - INFO("New request detected ! : \"%s\"\n", data); - } - } else { - INFO("Unrecognised data detected : \"%s\"\n", data); - } - asteriskReceivedOnce = false; - data[0] = 0; - curPos = 0; - CRLF = 1; - } - } - } -// else { - Thread::yield(); -// } - } -} - -void HttpServer::serveRequests() -{ - HTTPConnection::HTTPMessage *myMessage = new HTTPConnection::HTTPMessage; - - INFO("Server running\n"); - - while(1) { - INFO("Listening for new request !"); - osEvent evt = m_requestQueue.get(); - if (evt.status == osEventMessage) { - request_msg_t* pMsg = (request_msg_t*)evt.value.p; - m_worker->set_priority(osPriorityBelowNormal); - Thread::yield(); - switch(pMsg->requestType) { - case msg_get: - INFO("Server received GET message !"); - myMessage->request = HTTP_RT_GET; - myMessage->uri = pMsg->requestUri; - HandleRequest(myMessage); - Thread::yield(); - break; - - case msg_post: - case msg_put: - case msg_head: - case msg_delete: - case msg_trace: - case msg_options: - case msg_connect: - default: - break; - } - m_worker->set_priority(osPriorityNormal); - m_requestPool.free(pMsg); - } - Thread::yield(); - } -} - -bool HttpServer::parseRequest(char *request) -{ - // dissect into : path, file[, [arg, value]1..N ] as "/path/file?arg1=val1&arg2=val2... - // first check for questionmark sign to separate the file and path from any arguments - char* path = request; - char* file = NULL; - char* arglist = NULL; - - char* lastPathSep = NULL; - while(*request) { - if (*request == '/' ) - lastPathSep = request; - if (*request == '?') { - *request++ = 0; - arglist = request; - } - } - - if (arglist == NULL) { - INFO("Request does not have parameters !"); - } - - if (lastPathSep == NULL) - return false; // no path provided !!!! - - // now, whatever is provided to the left including the slash is the 'path', the part to the right is the file. caution: the file may be left blank ! - if (lastPathSep != 0) { - // 2 cases to handle : - // 1. : "/blah/" or "/blah/blub/" --> path = "/blah/", file = "index.html" - // 2. : "/blah/blub" or "/blah/blub/blubber" --> path = "/blah/", file = "blub" - } else { - // 2 cases to handle : - // 1. : "/" --> path = "/", file = "index.html" - // 2. : "/blah" --> path = "/", file = "blah" - } - return true; -} - - -void HttpServer::listen_thread(const void *params) -{ - HttpServer* pSvr = (HttpServer*)params; - - pSvr->listenForRequests(); -} - -void HttpServer::worker_thread(const void * params) -{ - HttpServer* pSvr = (HttpServer*)params; - - pSvr->serveRequests(); -} - - - - -static const char* szStdErrorPage = "<HTML><HEAD><META content=\"text/html\" http-equiv=Content-Type></HEAD><BODY><h1>Error 404</h1><P>This resource is not available<P></BODY></HTML>\r\n\r\n"; - -void HttpServer::StdErrorHandler(HTTPConnection::HTTPMessage& msg) -{ - char echoHeader[256]; - sprintf(echoHeader,"HTTP/1.0 404 Fail\r\nConnection: close\r\nContent-Length: %d\r\nContent-Type: text/html\r\nServer: mbed embedded\r\n\n\r",strlen(szStdErrorPage)); - - Wifly::getInstance()->sendData(echoHeader, strlen(echoHeader)); - Wifly::getInstance()->sendData((char*)szStdErrorPage, strlen(szStdErrorPage)); -} - -void HttpServer::HandleRequest(HTTPConnection::HTTPMessage* pmsg) -{ - static std::string localPath; - static std::map<std::string, HTTPRequestHandler*(*)(const char*, const char*, HTTPConnection::HTTPMessage&), handlersComp>::const_iterator it; - - INFO("Trying to handle request"); - // Iterate through registered handlers and check if the handler's path is a subset of the requested uri. - for (it = m_lpHandlers.begin() ; it != m_lpHandlers.end() ; it++) { - // check if this entries' path is fully contained at the beginning of the requested path - std::string curpth = it->first; - - if (pmsg->uri.find(curpth) == 0) { - // firts matching handler found, we just take it and we'll be happy - localPath = pmsg->uri.substr(curpth.length()); - break; - } - } - - if (it == m_lpHandlers.end()) { - // There is no such handler, so return invalid - INFO("Webrequest left unhandled."); - - m_pErrorHandler(*pmsg); - } else { - // Valid handler was found - INFO("Routing webrequest !"); - // Instantiate the handler object (handling will be done from withing the object's constructor - HTTPRequestHandler *phdl = (*it->second)(it->first.c_str(), localPath.c_str(), *pmsg); - // now we can delete the object, because handling is completed. - if (phdl != NULL) - delete phdl; - } -} -
--- a/HttpServer.h Wed Jun 26 21:13:55 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,132 +0,0 @@ -#ifndef __HTTPSERVER_H__ -#define __HTTPSERVER_H__ - -#include "Wifly.h" -#include "rtos.h" -#include "HTTPConnection.h" -#include "HTTPRequestHandler.h" - -#include <string> -#include <map> - -typedef enum { - msg_get, - msg_post, - msg_head, - msg_put, - msg_delete, - msg_trace, - msg_options, - msg_connect -} msg_t; - - -typedef struct { - msg_t requestType; - char requestUri[256]; -// map<string, string> messageHeaders; -} request_msg_t; - -/** Typedefinition for a handler function -*/ -typedef void (*HTTPRequestHandlerFunction)(HTTPConnection::HTTPMessage&); - - -/** This is the non-blocking HTTP Server class. The idea behind this class is as follows: - * the user may instantiate the class and initialize it. Once the server is setup and - * listening, the server will stay in an endless loop and keep on listening for new - * connections and for new HTTP requests. Once a request is received it will be placed - * in a queue. The queue itself will be handled in a separate task. - */ -class HttpServer : public Wifly -{ - Thread *m_listener; - Thread *m_worker; - - request_msg_t* checkMessageReceived(char *); - void processMessageHeader(char* headerLine, char **fieldname, char **fieldvalue); - - public: - HttpServer(PinName tx, PinName rx, PinName reset, PinName tcp_status, const char * ssid, const char * phrase, Security sec, Wifly::WiflyBaudrate_t baud = Wifly::Wifly_115200); - ~HttpServer(); - - bool start(int port); - - virtual void handler_rx(void); - virtual void attach_rx(bool); - - virtual bool join(); - virtual int send(const char * str, int len, const char * ACK = NULL, char * res = NULL, int timeout = DEFAULT_WAIT_RESP_TIMEOUT); - - public: - - - /** - * Structure which will allow to order the stored handlers according to their associated path. - */ - 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)); - } - }; - /** The standard error handler function. - * @param msg : Request message data. - * @param tcp : Socket to be used for responding. - */ - static void StdErrorHandler(HTTPConnection::HTTPMessage& msg); - - - /** Internal function which processes a request and which will try to find the matching handler function - * for the given request. Please note that the function will search through the list of handlers, iterating - * from longest to shortest \c paths. If the registered \c path is a subset of the request the associated - * handler is considered as being a match. - * @param msg : Request message data. Contains the requested logical \c uri. - * @param tcp : Socket to be used for communication with the client. - */ - void HandleRequest(HTTPConnection::HTTPMessage* msg); - - /** Map of handler objects. Can be any object derived from \ref HTTPRequestHeader. Use the \ref addHandler function - * to register new handler objects. - */ - static map<string, HTTPRequestHandler* (*)(const char*, const char*, HTTPConnection::HTTPMessage&), handlersComp> m_lpHandlers; - - /** - * Adds a request handler to the handlers list. You will have to use one of the existing implementations. - * With each handler a \c uri or \c path is associated. Whenever a request is received the server will - * walk through all registered handlers and check which \c path is matching. - * @param T : class which will be instanciated to serve these requests for the associated \b path. - * @param path : request uri starting with this \c path will be served using this handler. - */ - template<typename T> - void addHandler(const char* path) - { m_lpHandlers[path] = &T::create; } - - /** - * Replaces the standard error Handler. The error Handler will be called everytime a request is not - * matching any of the registered \c paths or \c uris. - * @param hdlFunc: User specified handler function which will be used in error conditions. - */ - void addErrorHandler(HTTPRequestHandlerFunction hdlFunc) - { m_pErrorHandler = hdlFunc!=NULL ?hdlFunc : StdErrorHandler; } - HTTPRequestHandlerFunction m_pErrorHandler; - - protected: - bool bind(int port); - void listenForRequests(); - void serveRequests(); - - bool parseRequest(char *request); - - static void listen_thread(const void * parms); - static void worker_thread(const void * parms); -}; - -#endif // __HTTPSERVER_H__ \ No newline at end of file
--- a/WiFlyHTTPServer.lib Wed Jun 26 21:13:55 2013 +0000 +++ b/WiFlyHTTPServer.lib Wed Jun 26 22:41:58 2013 +0000 @@ -1,1 +1,1 @@ -http://mbed.org/users/leihen/code/WiFlyHTTPServer/#93ff322420b0 +http://mbed.org/users/leihen/code/WiFlyHTTPServer/#7f9fbfc18623