Single instance HTTP Server using WiFly Interface.
Dependents: WiFlyHTTPServerSample MultiThreadingHTTPServer
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
Generated on Tue Jul 12 2022 16:18:49 by 1.7.2