MythTV  0.26-pre
httpserver.cpp
Go to the documentation of this file.
00001 
00002 // Program Name: httpserver.cpp
00003 // Created     : Oct. 1, 2005
00004 //
00005 // Purpose     : HTTP 1.1 Mini Server Implmenetation
00006 //               Used for UPnp/AV implementation & status information
00007 //                                                                            
00008 // Copyright (c) 2005 David Blain <dblain@mythtv.org>
00009 //                                          
00010 // Licensed under the GPL v2 or later, see COPYING for details                    
00011 //
00013 
00014 // ANSI C headers
00015 #include <cmath>
00016 
00017 // POSIX headers
00018 #include <compat.h>
00019 #ifndef USING_MINGW
00020 #include <sys/utsname.h> 
00021 #endif
00022 
00023 // Qt headers
00024 #include <QScriptEngine>
00025 
00026 // MythTV headers
00027 #include "httpserver.h"
00028 #include "upnputil.h"
00029 #include "upnp.h" // only needed for Config... remove once config is moved.
00030 #include "compat.h"
00031 #include "mythdirs.h"
00032 #include "mythlogging.h"
00033 #include "htmlserver.h"
00034 
00037 //
00038 // HttpServer Class Implementation
00039 //
00042 
00043 QMutex   HttpServer::s_platformLock;
00044 QString  HttpServer::s_platform;
00045 
00047 //
00049 
00050 HttpServer::HttpServer(const QString sApplicationPrefix) :
00051     ServerPool(), m_sSharePath(GetShareDir()),
00052     m_pHtmlServer(new HtmlServerExtension(m_sSharePath, sApplicationPrefix)),
00053     m_threadPool("HttpServerPool"), m_running(true)
00054 {
00055     setMaxPendingConnections(20);
00056 
00057     // ----------------------------------------------------------------------
00058     // Build Platform String
00059     // ----------------------------------------------------------------------
00060     {
00061         QMutexLocker locker(&s_platformLock);
00062 #ifdef USING_MINGW
00063         s_platform = QString("Windows %1.%2")
00064             .arg(LOBYTE(LOWORD(GetVersion())))
00065             .arg(HIBYTE(LOWORD(GetVersion())));
00066 #else
00067         struct utsname uname_info;
00068         uname( &uname_info );
00069         s_platform = QString("%1 %2")
00070             .arg(uname_info.sysname).arg(uname_info.release);
00071 #endif
00072     }
00073 
00074     LOG(VB_UPNP, LOG_INFO, QString("HttpServer() - SharePath = %1")
00075             .arg(m_sSharePath));
00076 
00077     // -=>TODO: Load Config XML
00078     // -=>TODO: Load & initialize - HttpServerExtensions
00079 }
00080 
00082 //
00084 
00085 HttpServer::~HttpServer()
00086 {
00087     m_rwlock.lockForWrite();
00088     m_running = false;
00089     m_rwlock.unlock();
00090 
00091     m_threadPool.Stop();
00092 
00093     while (!m_extensions.empty())
00094     {
00095         delete m_extensions.takeFirst();
00096     }
00097 
00098     if (m_pHtmlServer != NULL)
00099         delete m_pHtmlServer;
00100 }
00101 
00103 //
00105 
00106 QString HttpServer::GetPlatform(void)
00107 {
00108     QMutexLocker locker(&s_platformLock);
00109     return s_platform;
00110 }
00111 
00113 //
00115 
00116 QScriptEngine* HttpServer::ScriptEngine()
00117 {
00118     return ((HtmlServerExtension *)m_pHtmlServer)->ScriptEngine();
00119 }
00120 
00122 //
00124 
00125 void HttpServer::newTcpConnection(int nSocket)
00126 {
00127     m_threadPool.startReserved(
00128         new HttpWorker(*this, nSocket),
00129         QString("HttpServer%1").arg(nSocket));
00130 }
00131 
00133 //
00135 
00136 void HttpServer::RegisterExtension( HttpServerExtension *pExtension )
00137 {
00138     if (pExtension != NULL )
00139     {
00140         m_rwlock.lockForWrite();
00141         m_extensions.append( pExtension );
00142 
00143         // Add to multimap for quick lookup.
00144 
00145         QStringList list = pExtension->GetBasePaths();
00146 
00147         for( int nIdx = 0; nIdx < list.size(); nIdx++)
00148             m_basePaths.insert( list[ nIdx ], pExtension );
00149 
00150         m_rwlock.unlock();
00151     }
00152 }
00153 
00155 //
00157 
00158 void HttpServer::UnregisterExtension( HttpServerExtension *pExtension )
00159 {
00160     if (pExtension != NULL )
00161     {
00162         m_rwlock.lockForWrite();
00163 
00164         QStringList list = pExtension->GetBasePaths();
00165 
00166         for( int nIdx = 0; nIdx < list.size(); nIdx++)
00167             m_basePaths.remove( list[ nIdx ], pExtension );
00168 
00169         m_extensions.removeAll(pExtension);
00170 
00171         delete pExtension;
00172 
00173         m_rwlock.unlock();
00174     }
00175 }
00176 
00178 //
00180 
00181 void HttpServer::DelegateRequest(HTTPRequest *pRequest)
00182 {
00183     bool bProcessed = false;
00184 
00185     m_rwlock.lockForRead();
00186 
00187     QList< HttpServerExtension* > list = m_basePaths.values( pRequest->m_sBaseUrl );
00188 
00189     for (int nIdx=0; nIdx < list.size() && !bProcessed; nIdx++ )
00190     {
00191         try
00192         {
00193             bProcessed = list[ nIdx ]->ProcessRequest(pRequest);
00194         }
00195         catch(...)
00196         {
00197             LOG(VB_GENERAL, LOG_ERR, QString("HttpServer::DelegateRequest - "
00198                                              "Unexpected Exception - "
00199                                              "pExtension->ProcessRequest()."));
00200         }
00201     }
00202 
00203 #if 0
00204     HttpServerExtensionList::iterator it = m_extensions.begin();
00205 
00206     for (; (it != m_extensions.end()) && !bProcessed; ++it)
00207     {
00208         try
00209         {
00210             bProcessed = (*it)->ProcessRequest(pRequest);
00211         }
00212         catch(...)
00213         {
00214             LOG(VB_GENERAL, LOG_ERR, QString("HttpServer::DelegateRequest - "
00215                                              "Unexpected Exception - "
00216                                              "pExtension->ProcessRequest()."));
00217         }
00218     }
00219 #endif
00220     m_rwlock.unlock();
00221 
00222     if (!bProcessed)
00223         bProcessed = m_pHtmlServer->ProcessRequest(pRequest);
00224 
00225     if (!bProcessed)
00226     {
00227         pRequest->m_eResponseType   = ResponseTypeHTML;
00228         pRequest->m_nResponseStatus = 404; 
00229     }
00230 }
00231 
00234 //
00235 // HttpWorkerThread Class Implementation
00236 //
00239 
00240 HttpWorker::HttpWorker(HttpServer &httpServer, int sock) :
00241     m_httpServer(httpServer), m_socket(sock), m_socketTimeout(10000)
00242 {
00243     m_socketTimeout = 1000 *
00244         UPnp::GetConfiguration()->GetValue("HTTP/KeepAliveTimeoutSecs", 10);
00245 }                  
00246 
00248 //
00250 
00251 void HttpWorker::run(void)
00252 {
00253 #if 0
00254     LOG(VB_UPNP, LOG_DEBUG,
00255         QString("HttpWorker::run() socket=%1 -- begin").arg(m_socket));
00256 #endif
00257 
00258     bool                    bTimeout   = false;
00259     bool                    bKeepAlive = true;
00260     BufferedSocketDevice   *pSocket    = NULL;
00261     HTTPRequest            *pRequest   = NULL;
00262 
00263     try
00264     {
00265         if ((pSocket = new BufferedSocketDevice( m_socket )) == NULL)
00266         {
00267             LOG(VB_GENERAL, LOG_ERR, "Error Creating BufferedSocketDevice");
00268             return;
00269         }
00270 
00271         pSocket->SocketDevice()->setBlocking( true );
00272 
00273         while (m_httpServer.IsRunning() && bKeepAlive && pSocket->IsValid())
00274         {
00275             bTimeout = false;
00276 
00277             int64_t nBytes = pSocket->WaitForMore(m_socketTimeout, &bTimeout);
00278             if (!m_httpServer.IsRunning())
00279                 break;
00280 
00281             if ( nBytes > 0)
00282             {
00283                 // ----------------------------------------------------------
00284                 // See if this is a valid request
00285                 // ----------------------------------------------------------
00286 
00287                 pRequest = new BufferedSocketDeviceRequest( pSocket );
00288                 if (pRequest != NULL)
00289                 {
00290                     if ( pRequest->ParseRequest() )
00291                     {
00292                         bKeepAlive = pRequest->GetKeepAlive();
00293 
00294                         // ------------------------------------------------------
00295                         // Request Parsed... Pass on to Main HttpServer class to 
00296                         // delegate processing to HttpServerExtensions.
00297                         // ------------------------------------------------------
00298 
00299                         if (pRequest->m_nResponseStatus != 401)
00300                             m_httpServer.DelegateRequest(pRequest);
00301                     }
00302                     else
00303                     {
00304                         LOG(VB_UPNP, LOG_ERR, "ParseRequest Failed.");
00305 
00306                         pRequest->m_nResponseStatus = 501;
00307                         bKeepAlive = false;
00308                     }
00309 
00310 #if 0
00311                     // Dump Request Header 
00312                     if (!bKeepAlive )
00313                     {
00314                         for ( QStringMap::iterator it  = pRequest->m_mapHeaders.begin(); 
00315                                                    it != pRequest->m_mapHeaders.end(); 
00316                                                  ++it ) 
00317                         {  
00318                             LOG(VB_GENERAL, LOG_DEBUG, QString("%1: %2") 
00319                                 .arg(it.key()) .arg(it.data()));
00320                         }
00321                     }
00322 #endif
00323 
00324                     // -------------------------------------------------------
00325                     // Always MUST send a response.
00326                     // -------------------------------------------------------
00327 
00328                     if (pRequest->SendResponse() < 0)
00329                     {
00330                         bKeepAlive = false;
00331                         LOG(VB_UPNP, LOG_ERR,
00332                             QString("socket(%1) - Error returned from "
00333                                     "SendResponse... Closing connection")
00334                                 .arg(m_socket));
00335                     }
00336 
00337                     // -------------------------------------------------------
00338                     // Check to see if a PostProcess was registered
00339                     // -------------------------------------------------------
00340 
00341                     if ( pRequest->m_pPostProcess != NULL )
00342                         pRequest->m_pPostProcess->ExecutePostProcess();
00343 
00344                     delete pRequest;
00345                     pRequest = NULL;
00346                 }
00347                 else
00348                 {
00349                     LOG(VB_GENERAL, LOG_ERR,
00350                         "Error Creating BufferedSocketDeviceRequest");
00351                     bKeepAlive = false;
00352                 }
00353             }
00354             else
00355             {
00356                 bKeepAlive = false;
00357             }
00358         }
00359     }
00360     catch(...)
00361     {
00362         LOG(VB_GENERAL, LOG_ERR, 
00363             "HttpWorkerThread::ProcessWork - Unexpected Exception.");
00364     }
00365 
00366     if (pRequest != NULL)
00367         delete pRequest;
00368 
00369     pSocket->Close();
00370 
00371     delete pSocket;
00372     m_socket = 0;
00373 
00374 #if 0
00375     LOG(VB_UPNP, LOG_DEBUG, "HttpWorkerThread::run() -- end");
00376 #endif
00377 }
00378 
00379 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends