MOOS 0.2375
|
00001 00002 // 00003 // MOOS - Mission Oriented Operating Suite 00004 // 00005 // A suit of Applications and Libraries for Mobile Robotics Research 00006 // Copyright (C) 2001-2005 Massachusetts Institute of Technology and 00007 // Oxford University. 00008 // 00009 // This software was written by Paul Newman at MIT 2001-2002 and Oxford 00010 // University 2003-2005. email: pnewman@robots.ox.ac.uk. 00011 // 00012 // This file is part of a MOOS Core Component. 00013 // 00014 // This program is free software; you can redistribute it and/or 00015 // modify it under the terms of the GNU General Public License as 00016 // published by the Free Software Foundation; either version 2 of the 00017 // License, or (at your option) any later version. 00018 // 00019 // This program is distributed in the hope that it will be useful, 00020 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00021 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00022 // General Public License for more details. 00023 // 00024 // You should have received a copy of the GNU General Public License 00025 // along with this program; if not, write to the Free Software 00026 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 00027 // 02111-1307, USA. 00028 // 00030 00031 #include "MOOSGenLib/MOOSGenLibGlobalHelper.h" 00032 #include "MOOSGenLib/MOOSAssert.h" 00033 #include "MOOSLIB/XPCTcpSocket.h" 00034 #include "MOOSLIB/MOOSCommClient.h" 00035 #include "MOOSGenLib/MOOSLock.h" 00036 #include "MOOSLIB/MOOSException.h" 00037 00038 #ifndef _WIN32 00039 #include <signal.h> 00040 #endif 00041 00042 #include "HTTPConnection.h" 00043 #include <sstream> 00044 00045 class CHTMLTag 00046 { 00047 private: 00048 std::ostringstream & m_Stream; 00049 std::string m_sTag; 00050 std::string m_sOptions; 00051 00052 public: 00053 CHTMLTag(std::ostringstream & sStream , 00054 const std::string & sTag, 00055 const std::string & sOptions="", 00056 const std::string & sContents="", 00057 bool bNewLine=true):m_Stream(sStream),m_sTag(sTag) 00058 { 00059 Open(sStream,m_sTag,sOptions,sContents,bNewLine); 00060 } 00061 00062 ~CHTMLTag() 00063 { 00064 Close(m_Stream,m_sTag); 00065 } 00066 00067 static void Open(std::ostringstream & sStream, 00068 const std::string & sTag, 00069 const std::string & sOptions="", 00070 const std::string & sContents="", 00071 bool bNewLine=false) 00072 { 00073 std::string sQ = bNewLine? "\r\n" : ""; 00074 sStream<<"<"<<sTag<<" "<<sOptions<<">"<<sContents<<sQ; 00075 } 00076 00077 static void Close(std::ostringstream & sStream,const std::string & sTag) 00078 { 00079 sStream<<"</"<<sTag<<">\r\n"; 00080 } 00081 static std::string Print(const std::string & sTag, 00082 const std::string & sOptions="", 00083 const std::string & sContents="") 00084 { 00085 std::ostringstream sStream; 00086 Open(sStream,sTag,sOptions,sContents); 00087 Close(sStream,sTag); 00088 return sStream.str(); 00089 } 00090 00091 }; 00092 00093 00094 CHTTPConnection::CHTTPConnection(XPCTcpSocket * pSocket, 00095 CMOOSCommClient* pMOOSComms, 00096 CMOOSLock* pMOOSCommsLock):m_pSocket(pSocket),m_pMOOSComms(pMOOSComms),m_pMOOSCommsLock(pMOOSCommsLock) 00097 { 00098 00099 } 00100 00101 00102 bool CHTTPConnection::Run() 00103 { 00104 m_ServeThread.Initialise(_CB,this); 00105 00106 m_ServeThread.Start(); 00107 00108 return true; 00109 } 00110 00111 00112 bool CHTTPConnection::Serve() 00113 { 00114 00115 //ignore broken pipes as is standard for network apps 00116 #ifndef _WIN32 00117 signal(SIGPIPE,SIG_IGN); 00118 #endif 00119 00120 try 00121 { 00122 if(ReadRequest()) 00123 { 00124 if(!MakeWebPage()) 00125 { 00126 SendFailureHeader(); 00127 } 00128 else 00129 { 00130 SendHeader(); 00131 } 00132 00133 SendWebPage(); 00134 00135 } 00136 } 00137 catch(XPCException e) 00138 { 00139 MOOSTrace("Exception caught in HTTP Connection server :\n%s\n",e.sGetException()); 00140 UNUSED_PARAMETER(e); 00141 } 00142 00143 //all done - clean up 00144 //strange behaviour on some combinations of clients and hosts 00145 //closing socket too quickly (and I mean with a few uS) prevents final read of page even though 00146 //XPCSocket reports all data is sent 00147 MOOSPause(100); 00148 00149 m_pSocket->vCloseSocket(); 00150 00151 delete m_pSocket; 00152 00153 return true; 00154 } 00155 00156 00157 00158 bool CHTTPConnection::ReadRequest() 00159 { 00160 00161 m_Request.Clean(); 00162 00163 //first line should always be obvious 00164 std::string sLine; 00165 if(!ReadLine(sLine)) 00166 return false; 00167 00168 std::string sWhat = MOOSChomp(sLine," "); 00169 std::string sPath = MOOSChomp(sLine," "); 00170 std::string sProtocol = MOOSChomp(sLine," "); 00171 00172 //switch 00173 //get the file name 00174 //std::string sFile = sPath.substr(0,sPath.find_last_of("/")); 00175 00176 //this is a Poke form 00177 std::string sFocus = MOOSChomp(sPath,"?"); 00178 std::string sFormContents = sPath; 00179 00180 //are we looking for a single variable? 00181 MOOSRemoveChars(sFocus,"/"); 00182 if(!sFocus.empty()) 00183 { 00184 m_sFocusVariable = sFocus; 00185 } 00186 else 00187 { 00188 m_sFocusVariable.clear(); 00189 } 00190 00191 00192 if(!sFormContents.empty()) 00193 { 00194 HandlePoke(sFormContents); 00195 } 00196 00197 //now read in rest of header lines 00198 do{ 00199 //fetch a line but with speed as we know a request is being formed 00200 if(ReadLine(sLine)) 00201 { 00202 //MOOSTrace("Rx: %s\n", sLine.c_str()); 00203 if(!sLine.empty()) 00204 { 00205 //we are in a header so parse a line 00206 m_Request.AddHeader(sLine); 00207 } 00208 } 00209 else 00210 { 00211 sLine.empty(); 00212 } 00213 }while(sLine.size()); 00214 00215 00216 00217 return true; 00218 } 00219 00220 void CHTTPConnection::SendLine(std::string sLine) 00221 { 00222 sLine+="\r\n"; 00223 int nSent = m_pSocket->iSendMessage((void*)(sLine.c_str()),sLine.size()); 00224 if(nSent!=sLine.size()) 00225 MOOSTrace("Failed tcp/ip send\n"); 00226 } 00227 00228 void CHTTPConnection::SendString(std::string sLine) 00229 { 00230 m_pSocket->iSendMessage((void*)(sLine.c_str()),sLine.size()); 00231 } 00232 00233 00234 00235 bool CHTTPConnection::ReadLine(std::string & sLine) 00236 { 00237 sLine.clear(); 00238 00239 try 00240 { 00241 char t = '\0'; 00242 while(1) 00243 { 00244 int nRead = m_pSocket->iRecieveMessage(&t,sizeof(t),0); 00245 if(nRead==1) 00246 { 00247 if(t!='\r') 00248 { 00249 sLine+=t; 00250 } 00251 else 00252 { 00253 break; 00254 } 00255 } 00256 if(nRead==0) 00257 { 00258 //time out or graceful closure 00259 return false; 00260 } 00261 } 00262 00263 } 00264 catch(XPCException e) 00265 { 00266 UNUSED_PARAMETER(e); 00267 MOOSAssert(0); 00268 } 00269 00270 MOOSRemoveChars(sLine,"\r\n"); 00271 00272 //MOOSTrace(sLine+"\n"); 00273 return true; 00274 } 00275 00276 bool CHTTPConnection::SendWebPage() 00277 { 00278 SendLine(m_sWebPage); 00279 00280 return true; 00281 } 00282 00283 char CharFromHex (std::string a) 00284 { 00285 std::istringstream k (a); 00286 int Z; 00287 k >> std::hex >> Z; 00288 00289 return char (Z); // cast to char and return 00290 } 00291 00292 std::string Decode(std::string Text) 00293 { 00294 00295 std::string::size_type Pos; 00296 std::string Hex; 00297 while (std::string::npos != (Pos = Text.find('%'))) 00298 { 00299 Hex = Text.substr(Pos + 1, 2); 00300 Text.replace(Pos, 3, 1, CharFromHex(Hex)); 00301 } 00302 return Text; 00303 00304 } 00305 00306 00307 bool CHTTPConnection::HandlePoke(std::string sPokeURL) 00308 { 00309 //& seperates fields, + is space, 00310 std::map<std::string,std::string> Token2ValMap; 00311 while(!sPokeURL.empty()) 00312 { 00313 std::string sPair =MOOSChomp(sPokeURL,"&"); 00314 std::string sToken = MOOSChomp(sPair,"="); 00315 Token2ValMap[sToken] = sPair; 00316 } 00317 00318 00319 if(m_sFocusVariable.empty()) 00320 { 00321 //this is a full webpage so the user shoudl have hit the Poke button 00322 if(Token2ValMap.find("VariableToPoke")==Token2ValMap.end()) 00323 return false; 00324 if(Token2ValMap.find("DoPoke")==Token2ValMap.end()) 00325 return false; 00326 00327 } 00328 else 00329 { 00330 Token2ValMap["VariableToPoke"] =m_sFocusVariable; 00331 } 00332 00333 if(Token2ValMap.find("NewValue")==Token2ValMap.end()) 00334 return false; 00335 00336 std::string sVar = Token2ValMap["VariableToPoke"]; 00337 std::string sWhat = Token2ValMap["NewValue"]; 00338 00339 00340 m_pMOOSCommsLock->Lock(); 00341 { 00342 if(MOOSIsNumeric(sWhat)) 00343 { 00344 m_pMOOSComms->Notify(sVar,atof(sWhat.c_str())); 00345 } 00346 else 00347 { 00348 m_pMOOSComms->Notify(sVar,Decode(sWhat)); 00349 } 00350 } 00351 m_pMOOSCommsLock->UnLock(); 00352 00353 return true; 00354 } 00355 00356 00357 bool CHTTPConnection::MakeWebPage() 00358 { 00359 //we can insert a quick check here to makes sure we aren't being 00360 //asked for things loke favicon.ico 00361 if(MOOSStrCmp(m_sFocusVariable, "favicon.ico")) 00362 { 00363 m_sWebPage = "Not Found"; 00364 return false; 00365 } 00366 00367 00368 m_pMOOSCommsLock->Lock(); 00369 00370 try 00371 { 00372 if(!m_pMOOSComms->IsConnected()) 00373 throw CMOOSException("No DB Connection"); 00374 00375 MOOSMSG_LIST MsgList; 00376 if(!m_pMOOSComms->ServerRequest("ALL",MsgList)) 00377 throw CMOOSException("Failed ServerRequest"); 00378 00379 00380 std::ostringstream wp; 00381 { 00382 { 00383 CHTMLTag HTML(wp,"HTML","style=\"font-family:arial\""); 00384 { 00385 CHTMLTag Head(wp,"HEAD"); 00386 wp<<"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n"; 00387 //wp<<"<meta http-equiv=\"refresh\" content=\"0.5;URL=http://localhost:9080\">\n"; 00388 CHTMLTag Title(wp,"TITLE","","MOOSDB"); 00389 } 00390 //body tag 00391 CHTMLTag BODY(wp,"BODY","BGCOLOR=white TEXT=black "); 00392 00393 CHTMLTag CenteredTable(wp,"TABLE","width=\"100%\" height=\"100%\""); 00394 CHTMLTag CellRow(wp,"TR"); 00395 CHTMLTag CellData(wp,"TD","valign=\"middle\" align=\"center\""); 00396 00397 00398 if(m_sFocusVariable.empty()) 00399 { 00400 //we have no focus variable eg localhost:9080/DB_TIME 00401 //so show the whole load 00402 BuildFullDBWebPageContents(wp,MsgList); 00403 } 00404 else 00405 { 00406 //only interested in a single variable... 00407 BuildSingleVariableWebPageContents(wp,MsgList); 00408 } 00409 } 00410 00411 m_sWebPage = wp.str(); 00412 00413 //MOOSTrace(m_sWebPage); 00414 } 00415 00416 } 00417 catch(CMOOSException e) 00418 { 00419 m_pMOOSCommsLock->UnLock(); 00420 return false; 00421 } 00422 00423 //success 00424 m_pMOOSCommsLock->UnLock(); 00425 return true; 00426 00427 00428 } 00429 00430 bool CHTTPConnection::BuildSingleVariableWebPageContents( std::ostringstream & wp,MOOSMSG_LIST & MsgList) 00431 { 00432 // tell user what they are looking at 00433 wp<<CHTMLTag::Print("H1","ALIGN=middle", 00434 MOOSFormat("\"%s\" in MOOSDB : %s ", 00435 m_sFocusVariable.c_str(), 00436 m_pMOOSComms->GetDescription().c_str())); 00437 00438 00439 CMOOSMsg Msg; 00440 if(m_pMOOSComms->PeekMail(MsgList,m_sFocusVariable,Msg)) 00441 { 00442 00443 { 00444 CHTMLTag Table(wp,"TABLE","BORDER=1"); 00445 00446 //set up table headers... 00447 { 00448 CHTMLTag Row(wp,"TR"); 00449 wp<<CHTMLTag::Print("TH","","Value"); 00450 wp<<CHTMLTag::Print("TH","","Time"); 00451 wp<<CHTMLTag::Print("TH","","Freq"); 00452 wp<<CHTMLTag::Print("TH","","Source"); 00453 wp<<CHTMLTag::Print("TH","","Community"); 00454 } 00455 00456 //data row 00457 { 00458 CHTMLTag Row(wp,"TR"); 00459 { 00460 CHTMLTag Form(wp,"FORM","action='/"+m_sFocusVariable+"'"); 00461 00462 std::string sControl = MOOSFormat("<input name=NewValue value=%s>",Msg.GetAsString().c_str()); 00463 wp<<CHTMLTag::Print("TD","",sControl); 00464 } 00465 00466 wp<<CHTMLTag::Print("TD","",MOOSFormat("%.3f",Msg.GetTime())); 00467 wp<<CHTMLTag::Print("TD","",MOOSFormat("%.3f",Msg.m_dfVal2)); 00468 wp<<CHTMLTag::Print("TD","",Msg.GetSource()); 00469 wp<<CHTMLTag::Print("TD","",Msg.GetCommunity()); 00470 } 00471 } 00472 00473 wp<<CHTMLTag::Print("p","","edit value box and press return to poke (set) this variable"); 00474 00475 } 00476 else 00477 { 00478 wp<<CHTMLTag::Print("H2","ALIGN=middle,TEXT=red",MOOSFormat("The variable %s does not exist in this DB!",m_sFocusVariable.c_str())); 00479 } 00480 00481 //add a refresh button 00482 wp<<"<p>\r\n"; 00483 00484 00485 wp<<CHTMLTag::Print("A","href = /","home"); 00486 wp<<CHTMLTag::Print("A","href = /"+m_sFocusVariable,"refresh"); 00487 00488 00489 return true; 00490 } 00491 00492 bool CHTTPConnection::BuildFullDBWebPageContents( std::ostringstream & wp,MOOSMSG_LIST & MsgList) 00493 { 00494 00495 // tell user what they are looking at 00496 wp<<CHTMLTag::Print("H1","ALIGN=middle","Contents of MOOSDB : "+m_pMOOSComms->GetDescription()); 00497 00498 { 00499 CHTMLTag Table(wp,"TABLE","BORDER=1 WIDTH=\"80%\" "); 00500 00501 //set up table headers... 00502 { 00503 CHTMLTag Row(wp,"TR"); 00504 wp<<CHTMLTag::Print("TH","","Name"); 00505 wp<<CHTMLTag::Print("TH","","Time"); 00506 wp<<CHTMLTag::Print("TH","","Type"); 00507 wp<<CHTMLTag::Print("TH","","Freq"); 00508 wp<<CHTMLTag::Print("TH","","Source"); 00509 wp<<CHTMLTag::Print("TH","","Community"); 00510 wp<<CHTMLTag::Print("TH","","Value"); 00511 00512 00513 00514 } 00515 00516 MOOSMSG_LIST::iterator q; 00517 00518 for(q = MsgList.begin(); q!=MsgList.end();q++) 00519 { 00520 CMOOSMsg & rMsg = *q; 00521 00522 std::string sT; 00523 switch(rMsg.m_cDataType) 00524 { 00525 case MOOS_STRING: 00526 sT = "$"; 00527 break; 00528 case MOOS_DOUBLE: 00529 sT = "double"; 00530 break; 00531 case MOOS_BINARY_STRING: 00532 sT = "binary"; 00533 break; 00534 case MOOS_NOT_SET: 00535 sT = "pending"; 00536 break; 00537 } 00538 00539 CHTMLTag Row(wp,"TR"); 00540 wp<<CHTMLTag::Print("TD","valign=\"left\"",CHTMLTag::Print("A","href = /"+rMsg.GetName(),rMsg.GetName())); 00541 wp<<CHTMLTag::Print("TD","valign=\"left\"",MOOSFormat("%.3f",rMsg.GetTime())); 00542 wp<<CHTMLTag::Print("TD","valign=\"left\"",MOOSFormat("%s",sT.c_str() ) ); 00543 wp<<CHTMLTag::Print("TD","valign=\"left\"",MOOSFormat("%.3f",rMsg.m_dfVal2)); 00544 wp<<CHTMLTag::Print("TD","valign=\"left\"",rMsg.GetSource()); 00545 wp<<CHTMLTag::Print("TD","valign=\"left\"",rMsg.GetCommunity()); 00546 wp<<CHTMLTag::Print("TD","valign=\"left\" style=\"WORD-BREAK:BREAK-ALL\"",rMsg.GetAsString().c_str()); 00547 00548 } 00549 00550 } 00551 00552 00553 wp<<CHTMLTag::Print("p","","Click on a name column for individual variable pages. <A href = /> refresh </A>"); 00554 00555 00556 //make a Poke table 00557 { 00558 //wp<<CHTMLTag::Print("h2","","Poke"); 00559 00560 CHTMLTag Form(wp,"FORM","action='/'"); 00561 { 00562 CHTMLTag Table(wp,"TABLE"); 00563 { 00564 { 00565 CHTMLTag Row(wp,"TR"); 00566 wp<<CHTMLTag::Print("TH","align=\"left\"","New Name"); 00567 wp<<CHTMLTag::Print("TH","align=\"left\"","New Value"); 00568 } 00569 00570 { 00571 CHTMLTag Row(wp,"TR"); 00572 wp<<CHTMLTag::Print("TD","","<input name=VariableToPoke>"); 00573 wp<<CHTMLTag::Print("TD","","<input name=NewValue>"); 00574 wp<<CHTMLTag::Print("TD","","<input type=submit name=DoPoke value=Poke>"); 00575 } 00576 } 00577 } 00578 00579 wp<<CHTMLTag::Print("p","","Add a new MOOS variable or change an existing one"); 00580 } 00581 00582 00583 return true; 00584 } 00585 00586 00587 bool CHTTPConnection::SendFailureHeader() 00588 { 00589 SendLine("HTTP/1.1 404 Not Found"); 00590 SendLine("NONE"); 00591 SendLine(""); 00592 00593 return true; 00594 } 00595 00596 bool CHTTPConnection::SendHeader() 00597 { 00598 SendLine("HTTP/1.1 200 OK"); 00599 SendLine("Content-Type: text/html; charset=ISO-8859-1"); 00600 SendLine(MOOSFormat("Content-Length: %d",m_sWebPage.size())); 00601 SendLine(""); 00602 00603 return true; 00604 } 00605 bool CHTTPConnection::HasCompleted() 00606 { 00607 return !m_ServeThread.IsThreadRunning(); 00608 }