Single instance HTTP Server using WiFly Interface.

Dependents:   WiFlyHTTPServerSample MultiThreadingHTTPServer

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers HttpServer.cpp Source File

HttpServer.cpp

00001 #include "mbed.h"
00002 #include "HttpServer.h"
00003 
00004 #define DEBUG
00005 #include "debug.h"
00006 
00007 
00008 #define EVENT_DATA_READY            0x05
00009 
00010 DigitalOut  ledRX(LED4);
00011 
00012 
00013 typedef struct {
00014     char c;
00015 } message_t;
00016 
00017 Queue<char, 256>    m_queue;
00018 
00019 typedef struct {
00020     const char* method;
00021     msg_t   type;
00022 } methodType_t;
00023 
00024 const methodType_t supportedOps[] = {
00025     { "GET", msg_get },
00026     { "POST", msg_post },
00027     { "PUT", msg_put },
00028     { "HEAD", msg_head},
00029     { "CONNECT", msg_connect},
00030     { "DELETE", msg_delete},
00031     { "TRACE", msg_trace},
00032     { "OPTIONS", msg_options}
00033 };
00034 
00035 
00036 Queue<request_msg_t, 5>         m_requestQueue;     //  Do not allow more than 5 concurrent requests
00037 MemoryPool<request_msg_t, 5>    m_requestPool;
00038 
00039 map<string, string>     messageHeaders;
00040 
00041 map<string, HTTPRequestHandler* (*)(const char*, const char*, HTTPConnection::HTTPMessage&), HttpServer::handlersComp>   HttpServer::m_lpHandlers;
00042 
00043 
00044 
00045 /* Constructor will create and initialize all objects excep the threads */
00046 HttpServer::HttpServer(PinName tx, PinName rx, PinName rst, PinName tcp_status, const char * ssid, const char * phrase, Security sec, Wifly::WiflyBaudrate_t baud)
00047     : Wifly(tx, rx, rst, tcp_status, ssid, phrase, sec, baud), m_listener(NULL), m_worker(NULL)
00048 {
00049     INFO("Initializing wifly\n");
00050     //  Initialize the wifly wlan device
00051     reset();
00052 
00053     state.dhcp = true;
00054     INFO("Connecting to network...");
00055     //  Try join the network
00056     while(!join()) {
00057         INFO("Failed to connect. Trying again\n");
00058         reset();
00059     }
00060     INFO("connected\n");
00061 }
00062 
00063 HttpServer::~HttpServer()
00064 {
00065     if (m_listener) {
00066         m_listener->terminate();
00067         delete m_listener;
00068     }
00069     if (m_worker) {
00070         m_worker->terminate();
00071         delete m_worker;
00072     }
00073 }
00074 
00075 
00076 bool HttpServer::start(int port)
00077 {
00078     //  Bind to that port
00079     if (!bind(port)) {
00080         ERR("Failed to bind to port %d\n", port);
00081         return false;
00082     }
00083 
00084     //  Start the child threads
00085     m_worker = new Thread(HttpServer::worker_thread, NULL, osPriorityAboveNormal, DEFAULT_STACK_SIZE*4);
00086     if (m_worker == NULL) {
00087         ERR("Failed to start server thread !\n");
00088         return false;
00089     }
00090 
00091     m_listener = new Thread(&HttpServer::listen_thread, NULL, osPriorityAboveNormal, DEFAULT_STACK_SIZE*2);
00092     if (m_listener == NULL) {
00093         ERR("Failed to start listener thread !\n");
00094         m_worker->terminate();
00095         delete m_worker;
00096         m_worker = NULL;
00097         return false;
00098     }
00099 
00100     return true;
00101 }
00102 
00103 
00104 
00105 
00106 
00107 bool HttpServer::bind(int port)
00108 {
00109     char cmd[20];
00110 
00111     // set TCP protocol
00112     setProtocol(TCP);
00113 
00114     // set local port
00115     sprintf(cmd, "set i l %d\r", port);
00116     if (!sendCommand(cmd, "AOK"))
00117         return false;
00118 
00119     // save
00120     if (!sendCommand("save\r", "Stor"))
00121         return false;
00122 
00123     // reboot
00124     reboot();
00125 
00126     // connect the network
00127     if (isDHCP()) {
00128         if (!sendCommand("join\r", "DHCP=ON", NULL, 10000))
00129             return false;
00130     } else {
00131         if (!sendCommand("join\r", "Associated", NULL, 10000))
00132             return false;
00133     }
00134 
00135     // exit
00136     exit();
00137 
00138     Thread::wait(200);
00139     flush();
00140 
00141     return true;
00142 }
00143 
00144 DigitalOut Led2(LED2);
00145 
00146 void HttpServer::handler_rx(void)
00147 {
00148     static char sequence = 0;
00149     //read characters
00150     while (wifi.readable()) {
00151         char c = LPC_UART3->RBR;
00152         ledRX = !ledRX;
00153         switch(sequence) {
00154             case 0 : if (c == 'G') sequence = 1; break;
00155             case 1 : if (c == 'E') sequence = 2; break;
00156             case 2 : if (c == 'T') sequence = 0; Led2 = !Led2;break;
00157             default: break;
00158         }
00159         m_queue.put((char*)(int)c);
00160     }
00161 }
00162 
00163 
00164 void HttpServer::attach_rx(bool callback)
00165 {
00166     if (!callback)
00167         wifi.attach(NULL);
00168     else
00169         wifi.attach(this, &HttpServer::handler_rx);
00170 }
00171 
00172 
00173 bool HttpServer::join()
00174 {
00175     return Wifly::join();
00176 }
00177 
00178 int HttpServer::send(const char * str, int len, const char * ACK, char * res, int timeout)
00179 {
00180     return Wifly::send(str, len, ACK, res, timeout);
00181 }
00182 
00183 request_msg_t* HttpServer::checkMessageReceived(char *data)
00184 {
00185     INFO("Checking for new HTTP request !\n");
00186     char *req = data;
00187     char *uri = NULL;
00188     char *ver = NULL;
00189     while( *data ) {
00190         if (*data == ' ') {
00191             *data = 0;
00192             if (uri == NULL) {
00193                 uri = data+1;
00194             } else {
00195                 ver = data+1;
00196                 break;
00197             }
00198         }
00199         data++;
00200     }
00201 
00202     INFO("Detected : %s, %s, %s\n", req, uri, ver);
00203 
00204     if ((req != NULL) && (uri != NULL) && (ver != NULL) ) {
00205         for (int i = 0 ; i < sizeof(supportedOps) / sizeof(methodType_t) ; i++) {
00206             if (strcmp(supportedOps[i].method, req) == 0) {
00207                 //  found the request
00208                 INFO("Request valid !!!\n");
00209                 request_msg_t* pmsg = m_requestPool.alloc();
00210                 pmsg->requestType = supportedOps[i].type;
00211                 strncpy(pmsg->requestUri, uri, 255);
00212                 return pmsg;
00213             }
00214         }
00215     }
00216 
00217     INFO("Invalid request \"%s\"\n", req);
00218     return NULL;
00219 }
00220 
00221 void HttpServer::processMessageHeader(char* headerLine, char **fieldname, char **fieldvalue)
00222 {
00223     *fieldname = headerLine;
00224     *fieldvalue = NULL;
00225 
00226     while( *headerLine ) {
00227         if (*headerLine == ':') {
00228             *headerLine++ = 0;
00229             while(*headerLine == ' ') headerLine++;
00230             *fieldvalue = headerLine;
00231             return;
00232         }
00233         headerLine++;
00234     }
00235     return ;
00236 }
00237 
00238 
00239 void HttpServer::listenForRequests()
00240 {
00241     static char data[256];
00242     static int curPos = 0;
00243     int CRLF = 0;
00244     int         m_openConnections = 0;
00245 
00246     request_msg_t    *pMsg = NULL;
00247     INFO("Listener running\n");
00248     bool asteriskReceivedOnce = false;
00249     while(1) {
00250         osEvent evt = m_queue.get();
00251         if (evt.status == osEventMessage) {
00252             char c;
00253             c = (char)(int)evt.value.p;
00254             if ((c!='\n') && (c!='\r')) {
00255                 data[curPos++] = c;
00256                 data[curPos] = 0;
00257             }
00258             if (pMsg != NULL) {        //  request was detected and will further be processed completely
00259                 //  check for CRLF
00260                 if (c == '\n') {
00261                     CRLF++;
00262                     INFO("<CR>(%d)", CRLF);
00263                     if (CRLF == 2) {    //  all message headers received, so send message and be ready for new one
00264                         CRLF = 0;
00265                         //  SPAWN MESSAGE
00266                         INFO("REQUEST COMPLETE --> Handing over to worker thread !\n\n\n\n");
00267                         m_requestQueue.put(pMsg);
00268                         data[0] = 0;
00269                         curPos = 0;
00270                         asteriskReceivedOnce = false;
00271                         pMsg = NULL;
00272                     } else {            //  must be a new header
00273 //                        char *name, *value;
00274 //                        INFO("Processing Header !\"%s\"", data);
00275 /*                        processMessageHeader(data, &name, &value);
00276                         if (strncmp(name, "Content-Length", 14 ) == 0) {
00277                             //  Data will be sent, be ready to receive
00278                         } else {
00279                             INFO("HEADER: Name=\"%s\", Value=\"%s\"", name, value);
00280                         }
00281 */                        data[0] = 0;
00282                         curPos = 0;
00283                     }
00284                 } else {
00285                     if (c != '\r')
00286                         CRLF = 0;
00287                         else
00288                         INFO("<LF>");
00289                 }
00290             } else if (c == '*') {
00291                 CRLF = 0;
00292                 if (asteriskReceivedOnce) {
00293                     //  could be an open, close or read command
00294                     if (curPos >= 6) {    // only need to process if data is large enough
00295                         if ( (data[curPos-6] == '*') && (data[curPos-5] == 'O') && (data[curPos-4] == 'P') && (data[curPos-3] == 'E') && (data[curPos-2] == 'N') && (data[curPos-1] == '*')) {
00296                             //  Add a connection
00297                             INFO("New connection opened (%d)...\n", ++m_openConnections);
00298                             data[0] = 0;
00299                             curPos = 0;
00300                         } else if ( (data[curPos-6] == '*') && (data[curPos-5] == 'C') && (data[curPos-4] == 'L') && (data[curPos-3] == 'O') && (data[curPos-2] == 'S') && (data[curPos-1] == '*')) {
00301                             //  close a connection
00302                             INFO("Connection was closed ...(%d)\n", --m_openConnections);
00303                             data[0] = 0;
00304                             curPos = 0;
00305                         }
00306                     }
00307                     asteriskReceivedOnce = false;
00308                 } else {        //  set the indicator so that next time we'll check for valid connection commands
00309                     asteriskReceivedOnce = true;
00310                 }
00311             } else {      //  first make sure that when no asterisk is received the asteriskReceivedOnce flag will be reset on each newline
00312                 if (c == '\n') {
00313                     if (m_openConnections > 0) {
00314                         //  Check to see if we received a valid request
00315                         pMsg = checkMessageReceived(data);
00316                         if (pMsg == NULL) {
00317                             //  not received valid stuff, so discard
00318                             INFO("Unrecognised data received : \"%s\"\n", data);
00319                         } else {
00320                             INFO("New request detected ! : \"%s\"\n", data);
00321                         }
00322                     } else {
00323                         INFO("Unrecognised data detected : \"%s\"\n", data);
00324                     }
00325                     asteriskReceivedOnce = false;
00326                     data[0] = 0;
00327                     curPos = 0;
00328                     CRLF = 1;
00329                 }
00330             }
00331         }
00332 //        else {
00333             Thread::yield();
00334 //        }
00335     }
00336 }
00337 
00338 void HttpServer::serveRequests()
00339 {
00340     HTTPConnection::HTTPMessage     *myMessage = new HTTPConnection::HTTPMessage;
00341     
00342     INFO("Server running\n");
00343 
00344     while(1) {
00345         INFO("Listening for new request !");
00346         osEvent evt = m_requestQueue.get();
00347         if (evt.status == osEventMessage) {
00348             request_msg_t* pMsg = (request_msg_t*)evt.value.p;
00349             m_worker->set_priority(osPriorityBelowNormal);
00350             Thread::yield();
00351             switch(pMsg->requestType) {
00352                 case    msg_get:
00353                     INFO("Server received GET message !");
00354                     myMessage->request = HTTP_RT_GET;
00355                     myMessage->uri = pMsg->requestUri;
00356                     HandleRequest(myMessage);
00357                     Thread::yield();
00358                     break;
00359 
00360                 case    msg_post:
00361                 case    msg_put:
00362                 case    msg_head:
00363                 case    msg_delete:
00364                 case    msg_trace:
00365                 case    msg_options:
00366                 case    msg_connect:
00367                 default:
00368                     break;
00369             }
00370             m_worker->set_priority(osPriorityNormal);
00371             m_requestPool.free(pMsg);
00372         }
00373         Thread::yield();
00374     }
00375 }
00376 
00377 bool HttpServer::parseRequest(char *request)
00378 {
00379     //  dissect into :   path, file[, [arg, value]1..N ]   as  "/path/file?arg1=val1&arg2=val2...
00380     //  first check for questionmark sign to separate the file and path from any arguments
00381     char* path = request;
00382     char* file = NULL;
00383     char* arglist = NULL;
00384 
00385     char* lastPathSep = NULL;
00386     while(*request) {
00387         if (*request == '/' )
00388             lastPathSep = request;
00389         if (*request == '?') {
00390             *request++ = 0;
00391             arglist = request;
00392         }
00393     }
00394 
00395     if (arglist == NULL) {
00396         INFO("Request does not have parameters !");
00397     }
00398 
00399     if (lastPathSep == NULL)
00400         return false;               //  no path provided !!!!
00401 
00402     //  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 !
00403     if (lastPathSep != 0) {
00404         //  2 cases to handle :
00405         //  1. :      "/blah/" or "/blah/blub/"                 --> path = "/blah/", file = "index.html"
00406         //  2. :      "/blah/blub" or "/blah/blub/blubber"      --> path = "/blah/", file = "blub"
00407     } else {
00408         //  2 cases to handle :
00409         //  1. :      "/"       --> path = "/", file = "index.html"
00410         //  2. :      "/blah"   --> path = "/", file = "blah"
00411     }
00412     return true;
00413 }
00414 
00415 
00416 void HttpServer::listen_thread(const void *params)
00417 {
00418     HttpServer* pSvr = (HttpServer*)params;
00419 
00420     pSvr->listenForRequests();
00421 }
00422 
00423 void HttpServer::worker_thread(const void * params)
00424 {
00425     HttpServer* pSvr = (HttpServer*)params;
00426 
00427     pSvr->serveRequests();
00428 }
00429 
00430 
00431 
00432 
00433 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";
00434 
00435 void HttpServer::StdErrorHandler(HTTPConnection::HTTPMessage& msg)
00436 {
00437     char echoHeader[256];
00438     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));
00439     
00440     Wifly::getInstance()->sendData(echoHeader, strlen(echoHeader));
00441     Wifly::getInstance()->sendData((char*)szStdErrorPage, strlen(szStdErrorPage));
00442 }
00443 
00444 void HttpServer::HandleRequest(HTTPConnection::HTTPMessage* pmsg)
00445 {
00446     static std::string localPath;
00447     static std::map<std::string, HTTPRequestHandler*(*)(const char*, const char*, HTTPConnection::HTTPMessage&), handlersComp>::const_iterator it;
00448 
00449     INFO("Trying to handle request");
00450     //  Iterate through registered handlers and check if the handler's path is a subset of the requested uri.
00451     for (it = m_lpHandlers.begin() ; it != m_lpHandlers.end() ; it++) {
00452         //  check if this entries' path is fully contained at the beginning of the requested path
00453         std::string curpth = it->first;
00454 
00455         if (pmsg->uri.find(curpth) == 0) {
00456             // firts matching handler found, we just take it and we'll be happy
00457             localPath = pmsg->uri.substr(curpth.length());
00458             break;
00459         }
00460     }
00461 
00462     if (it == m_lpHandlers.end()) {
00463         //  There is no such handler, so return invalid
00464         INFO("Webrequest left unhandled.");
00465 
00466         m_pErrorHandler(*pmsg);
00467     } else {
00468         //  Valid handler was found
00469         INFO("Routing webrequest !");
00470         //  Instantiate the handler object (handling will be done from withing the object's constructor
00471         HTTPRequestHandler *phdl = (*it->second)(it->first.c_str(), localPath.c_str(), *pmsg);
00472         //  now we can delete the object, because handling is completed.
00473         if (phdl != NULL)
00474             delete phdl;
00475     }
00476 }
00477