2013-09-27 12:14:26 -04:00
// HTTPServer.cpp
// Implements the cHTTPServer class representing a HTTP webserver that uses cListenThread and cSocketThreads for processing
# include "Globals.h"
# include "HTTPServer.h"
# include "HTTPMessage.h"
2013-09-27 13:34:46 -04:00
# include "HTTPConnection.h"
2013-09-28 13:30:25 -04:00
# include "HTTPFormParser.h"
2014-05-01 05:48:03 -04:00
# include "SslHTTPConnection.h"
2013-09-27 12:14:26 -04:00
// Disable MSVC warnings:
# if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable:4355) // 'this' : used in base member initializer list
# endif
2013-09-27 15:28:41 -04:00
class cDebugCallbacks :
2013-10-04 07:07:57 -04:00
public cHTTPServer : : cCallbacks ,
protected cHTTPFormParser : : cCallbacks
2013-09-27 15:28:41 -04:00
{
virtual void OnRequestBegun ( cHTTPConnection & a_Connection , cHTTPRequest & a_Request ) override
{
2014-02-28 10:26:23 -05:00
UNUSED ( a_Connection ) ;
2013-09-28 13:30:25 -04:00
if ( cHTTPFormParser : : HasFormData ( a_Request ) )
{
2013-10-04 07:07:57 -04:00
a_Request . SetUserData ( new cHTTPFormParser ( a_Request , * this ) ) ;
2013-09-28 13:30:25 -04:00
}
2013-09-27 15:28:41 -04:00
}
2014-04-01 10:36:00 -04:00
virtual void OnRequestBody ( cHTTPConnection & a_Connection , cHTTPRequest & a_Request , const char * a_Data , size_t a_Size ) override
2013-09-27 15:28:41 -04:00
{
2014-02-28 10:26:23 -05:00
UNUSED ( a_Connection ) ;
2013-09-28 13:30:25 -04:00
cHTTPFormParser * FormParser = ( cHTTPFormParser * ) ( a_Request . GetUserData ( ) ) ;
if ( FormParser ! = NULL )
{
FormParser - > Parse ( a_Data , a_Size ) ;
}
2013-09-27 15:28:41 -04:00
}
virtual void OnRequestFinished ( cHTTPConnection & a_Connection , cHTTPRequest & a_Request ) override
{
2013-09-28 13:30:25 -04:00
cHTTPFormParser * FormParser = ( cHTTPFormParser * ) ( a_Request . GetUserData ( ) ) ;
if ( FormParser ! = NULL )
{
if ( FormParser - > Finish ( ) )
{
cHTTPResponse Resp ;
Resp . SetContentType ( " text/html " ) ;
a_Connection . Send ( Resp ) ;
a_Connection . Send ( " <html><body><table border=1 cellspacing=0><tr><th>Name</th><th>Value</th></tr> \r \n " ) ;
for ( cHTTPFormParser : : iterator itr = FormParser - > begin ( ) , end = FormParser - > end ( ) ; itr ! = end ; + + itr )
{
a_Connection . Send ( Printf ( " <tr><td valign= \" top \" ><pre>%s</pre></td><td valign= \" top \" ><pre>%s</pre></td></tr> \r \n " , itr - > first . c_str ( ) , itr - > second . c_str ( ) ) ) ;
} // for itr - FormParser[]
a_Connection . Send ( " </table></body></html> " ) ;
return ;
}
// Parsing failed:
cHTTPResponse Resp ;
Resp . SetContentType ( " text/plain " ) ;
a_Connection . Send ( Resp ) ;
a_Connection . Send ( " Form parsing failed " ) ;
2013-09-28 14:06:35 -04:00
return ;
2013-09-28 13:30:25 -04:00
}
2013-10-04 14:28:30 -04:00
// Test the auth failure and success:
if ( a_Request . GetURL ( ) = = " /auth " )
{
if ( ! a_Request . HasAuth ( ) | | ( a_Request . GetAuthUsername ( ) ! = " a " ) | | ( a_Request . GetAuthPassword ( ) ! = " b " ) )
{
a_Connection . SendNeedAuth ( " MCServer WebAdmin " ) ;
return ;
}
}
2013-09-27 15:28:41 -04:00
cHTTPResponse Resp ;
Resp . SetContentType ( " text/plain " ) ;
a_Connection . Send ( Resp ) ;
a_Connection . Send ( " Hello, world " ) ;
}
2013-10-04 07:07:57 -04:00
virtual void OnFileStart ( cHTTPFormParser & a_Parser , const AString & a_FileName ) override
{
// TODO
}
2014-04-01 10:36:00 -04:00
virtual void OnFileData ( cHTTPFormParser & a_Parser , const char * a_Data , size_t a_Size ) override
2013-10-04 07:07:57 -04:00
{
// TODO
}
virtual void OnFileEnd ( cHTTPFormParser & a_Parser ) override
{
// TODO
}
2013-09-27 15:28:41 -04:00
} g_DebugCallbacks ;
2014-07-17 16:15:34 -04:00
////////////////////////////////////////////////////////////////////////////////
2013-09-27 15:28:41 -04:00
// cHTTPServer:
2013-09-27 12:14:26 -04:00
cHTTPServer : : cHTTPServer ( void ) :
2014-07-18 03:18:00 -04:00
m_ListenThreadIPv4 ( * this , cSocket : : IPv4 , " WebServer " ) ,
m_ListenThreadIPv6 ( * this , cSocket : : IPv6 , " WebServer " ) ,
2014-05-01 09:21:41 -04:00
m_Callbacks ( NULL )
2013-09-27 12:14:26 -04:00
{
2014-05-02 17:46:06 -04:00
}
cHTTPServer : : ~ cHTTPServer ( )
{
Stop ( ) ;
}
bool cHTTPServer : : Initialize ( const AString & a_PortsIPv4 , const AString & a_PortsIPv6 )
{
// Read the HTTPS cert + key:
2014-05-01 05:48:03 -04:00
AString CertFile = cFile : : ReadWholeFile ( " webadmin/httpscert.crt " ) ;
AString KeyFile = cFile : : ReadWholeFile ( " webadmin/httpskey.pem " ) ;
if ( ! CertFile . empty ( ) & & ! KeyFile . empty ( ) )
{
2014-05-01 09:21:41 -04:00
m_Cert . reset ( new cX509Cert ) ;
2014-05-01 05:48:03 -04:00
int res = m_Cert - > Parse ( CertFile . data ( ) , CertFile . size ( ) ) ;
2014-05-06 15:43:31 -04:00
if ( res = = 0 )
2014-05-01 05:48:03 -04:00
{
2014-05-01 09:21:41 -04:00
m_CertPrivKey . reset ( new cCryptoKey ) ;
2014-05-01 05:48:03 -04:00
int res2 = m_CertPrivKey - > ParsePrivate ( KeyFile . data ( ) , KeyFile . size ( ) , " " ) ;
if ( res2 ! = 0 )
{
// Reading the private key failed, reset the cert:
2014-05-02 17:46:06 -04:00
LOGWARNING ( " WebServer: Cannot read HTTPS certificate private key: -0x%x " , - res2 ) ;
2014-05-01 05:48:03 -04:00
m_Cert . reset ( ) ;
}
}
else
{
2014-05-02 17:46:06 -04:00
LOGWARNING ( " WebServer: Cannot read HTTPS certificate: -0x%x " , - res ) ;
2014-05-01 05:48:03 -04:00
}
}
2013-09-27 12:14:26 -04:00
2014-05-02 17:46:06 -04:00
// Notify the admin about the HTTPS / HTTP status
if ( m_Cert . get ( ) = = NULL )
{
2014-09-10 13:47:59 -04:00
LOGWARNING ( " WebServer: The server is running in unsecured HTTP mode. " ) ;
LOGINFO ( " Put a valid HTTPS certificate in file 'webadmin/httpscert.crt' and its corresponding private key to 'webadmin/httpskey.pem' (without any password) to enable HTTPS support " ) ;
2014-05-02 17:46:06 -04:00
}
else
{
LOGINFO ( " WebServer: The server is running in secure HTTPS mode. " ) ;
}
// Open up requested ports:
2013-09-27 12:14:26 -04:00
bool HasAnyPort ;
2014-07-04 11:52:52 -04:00
m_ListenThreadIPv4 . SetReuseAddr ( true ) ;
m_ListenThreadIPv6 . SetReuseAddr ( true ) ;
2013-10-05 17:08:16 -04:00
HasAnyPort = m_ListenThreadIPv4 . Initialize ( a_PortsIPv4 ) ;
HasAnyPort = m_ListenThreadIPv6 . Initialize ( a_PortsIPv6 ) | | HasAnyPort ;
2013-09-27 12:14:26 -04:00
if ( ! HasAnyPort )
{
return false ;
}
2013-09-27 15:28:41 -04:00
return true ;
}
bool cHTTPServer : : Start ( cCallbacks & a_Callbacks )
{
m_Callbacks = & a_Callbacks ;
2013-09-27 12:14:26 -04:00
if ( ! m_ListenThreadIPv4 . Start ( ) )
{
return false ;
}
if ( ! m_ListenThreadIPv6 . Start ( ) )
{
m_ListenThreadIPv4 . Stop ( ) ;
return false ;
}
return true ;
}
2013-09-27 15:28:41 -04:00
void cHTTPServer : : Stop ( void )
{
m_ListenThreadIPv4 . Stop ( ) ;
m_ListenThreadIPv6 . Stop ( ) ;
// Drop all current connections:
cCSLock Lock ( m_CSConnections ) ;
2013-10-06 10:40:28 -04:00
while ( ! m_Connections . empty ( ) )
2013-09-27 15:28:41 -04:00
{
2013-10-06 10:40:28 -04:00
m_Connections . front ( ) - > Terminate ( ) ;
2013-09-27 15:28:41 -04:00
} // for itr - m_Connections[]
}
2013-09-27 12:14:26 -04:00
void cHTTPServer : : OnConnectionAccepted ( cSocket & a_Socket )
{
2014-05-01 05:48:03 -04:00
cHTTPConnection * Connection ;
if ( m_Cert . get ( ) ! = NULL )
{
Connection = new cSslHTTPConnection ( * this , m_Cert , m_CertPrivKey ) ;
}
else
{
Connection = new cHTTPConnection ( * this ) ;
}
2013-09-27 12:14:26 -04:00
m_SocketThreads . AddClient ( a_Socket , Connection ) ;
cCSLock Lock ( m_CSConnections ) ;
m_Connections . push_back ( Connection ) ;
}
void cHTTPServer : : CloseConnection ( cHTTPConnection & a_Connection )
{
m_SocketThreads . RemoveClient ( & a_Connection ) ;
cCSLock Lock ( m_CSConnections ) ;
for ( cHTTPConnections : : iterator itr = m_Connections . begin ( ) , end = m_Connections . end ( ) ; itr ! = end ; + + itr )
{
if ( * itr = = & a_Connection )
{
m_Connections . erase ( itr ) ;
break ;
}
}
2013-10-06 10:40:28 -04:00
delete & a_Connection ;
2013-09-27 12:14:26 -04:00
}
2013-09-27 14:33:18 -04:00
void cHTTPServer : : NotifyConnectionWrite ( cHTTPConnection & a_Connection )
{
m_SocketThreads . NotifyWrite ( & a_Connection ) ;
}
2013-09-27 12:14:26 -04:00
void cHTTPServer : : NewRequest ( cHTTPConnection & a_Connection , cHTTPRequest & a_Request )
{
2013-09-27 15:28:41 -04:00
m_Callbacks - > OnRequestBegun ( a_Connection , a_Request ) ;
2013-09-27 12:14:26 -04:00
}
2014-04-01 10:36:00 -04:00
void cHTTPServer : : RequestBody ( cHTTPConnection & a_Connection , cHTTPRequest & a_Request , const char * a_Data , size_t a_Size )
2013-09-27 12:14:26 -04:00
{
2013-09-27 15:28:41 -04:00
m_Callbacks - > OnRequestBody ( a_Connection , a_Request , a_Data , a_Size ) ;
2013-09-27 12:14:26 -04:00
}
void cHTTPServer : : RequestFinished ( cHTTPConnection & a_Connection , cHTTPRequest & a_Request )
{
2013-09-27 15:28:41 -04:00
m_Callbacks - > OnRequestFinished ( a_Connection , a_Request ) ;
a_Connection . AwaitNextRequest ( ) ;
2013-09-27 12:14:26 -04:00
}