MOOS 0.2375
/home/toby/moos-ivp/MOOS-2375-Oct0611/Core/MOOSDB/HTTPConnection.cpp
Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines