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 ) ;
2015-07-29 11:04:03 -04:00
cHTTPFormParser * FormParser = reinterpret_cast < cHTTPFormParser * > ( a_Request . GetUserData ( ) ) ;
2014-10-20 16:55:07 -04:00
if ( FormParser ! = nullptr )
2013-09-28 13:30:25 -04:00
{
FormParser - > Parse ( a_Data , a_Size ) ;
}
2013-09-27 15:28:41 -04:00
}
virtual void OnRequestFinished ( cHTTPConnection & a_Connection , cHTTPRequest & a_Request ) override
{
2015-07-29 11:04:03 -04:00
cHTTPFormParser * FormParser = reinterpret_cast < cHTTPFormParser * > ( a_Request . GetUserData ( ) ) ;
2014-10-20 16:55:07 -04:00
if ( FormParser ! = nullptr )
2013-09-28 13:30:25 -04:00
{
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 " ) )
{
2015-09-25 04:14:17 -04:00
a_Connection . SendNeedAuth ( " Cuberite WebAdmin " ) ;
2013-10-04 14:28:30 -04:00
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
}
2015-05-19 06:50:59 -04:00
} ;
static cDebugCallbacks g_DebugCallbacks ;
2013-09-27 15:28:41 -04:00
2015-01-24 04:11:34 -05:00
////////////////////////////////////////////////////////////////////////////////
// cHTTPServerListenCallbacks:
class cHTTPServerListenCallbacks :
public cNetwork : : cListenCallbacks
{
public :
cHTTPServerListenCallbacks ( cHTTPServer & a_HTTPServer , UInt16 a_Port ) :
m_HTTPServer ( a_HTTPServer ) ,
m_Port ( a_Port )
{
}
protected :
/** The HTTP server instance that we're attached to. */
cHTTPServer & m_HTTPServer ;
/** The port for which this instance is responsible. */
UInt16 m_Port ;
// cNetwork::cListenCallbacks overrides:
virtual cTCPLink : : cCallbacksPtr OnIncomingConnection ( const AString & a_RemoteIPAddress , UInt16 a_RemotePort ) override
{
return m_HTTPServer . OnIncomingConnection ( a_RemoteIPAddress , a_RemotePort ) ;
}
virtual void OnAccepted ( cTCPLink & a_Link ) override { }
virtual void OnError ( int a_ErrorCode , const AString & a_ErrorMsg ) override
{
LOGWARNING ( " HTTP server error on port %d: %d (%s) " , m_Port , a_ErrorCode , a_ErrorMsg . c_str ( ) ) ;
}
} ;
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-10-20 16:55:07 -04:00
m_Callbacks ( nullptr )
2013-09-27 12:14:26 -04:00
{
2014-05-02 17:46:06 -04:00
}
cHTTPServer : : ~ cHTTPServer ( )
{
Stop ( ) ;
}
2015-01-24 04:11:34 -05:00
bool cHTTPServer : : Initialize ( void )
2014-05-02 17:46:06 -04:00
{
// 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
2014-10-20 16:55:07 -04:00
if ( m_Cert . get ( ) = = nullptr )
2014-05-02 17:46:06 -04:00
{
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. " ) ;
}
2013-09-27 15:28:41 -04:00
return true ;
}
2015-01-24 04:11:34 -05:00
bool cHTTPServer : : Start ( cCallbacks & a_Callbacks , const AStringVector & a_Ports )
2013-09-27 15:28:41 -04:00
{
m_Callbacks = & a_Callbacks ;
2015-01-24 04:11:34 -05:00
// Open up requested ports:
for ( auto port : a_Ports )
2013-09-27 12:14:26 -04:00
{
2015-01-25 10:25:15 -05:00
UInt16 PortNum ;
if ( ! StringToInteger ( port , PortNum ) )
2015-01-24 04:11:34 -05:00
{
LOGWARNING ( " WebServer: Invalid port value: \" %s \" . Ignoring. " , port . c_str ( ) ) ;
continue ;
}
auto Handle = cNetwork : : Listen ( PortNum , std : : make_shared < cHTTPServerListenCallbacks > ( * this , PortNum ) ) ;
if ( Handle - > IsListening ( ) )
{
m_ServerHandles . push_back ( Handle ) ;
}
} // for port - a_Ports[]
// Report success if at least one port opened successfully:
return ! m_ServerHandles . empty ( ) ;
2013-09-27 12:14:26 -04:00
}
2013-09-27 15:28:41 -04:00
void cHTTPServer : : Stop ( void )
{
2015-01-24 04:11:34 -05:00
for ( auto handle : m_ServerHandles )
2013-09-27 15:28:41 -04:00
{
2015-01-24 04:11:34 -05:00
handle - > Close ( ) ;
}
m_ServerHandles . clear ( ) ;
2013-09-27 15:28:41 -04:00
}
2015-01-24 04:11:34 -05:00
cTCPLink : : cCallbacksPtr cHTTPServer : : OnIncomingConnection ( const AString & a_RemoteIPAddress , UInt16 a_RemotePort )
2013-09-27 12:14:26 -04:00
{
2015-01-24 04:11:34 -05:00
UNUSED ( a_RemoteIPAddress ) ;
UNUSED ( a_RemotePort ) ;
2014-10-20 16:55:07 -04:00
if ( m_Cert . get ( ) ! = nullptr )
2014-05-01 05:48:03 -04:00
{
2015-01-24 04:11:34 -05:00
return std : : make_shared < cSslHTTPConnection > ( * this , m_Cert , m_CertPrivKey ) ;
2014-05-01 05:48:03 -04:00
}
else
{
2015-01-24 04:11:34 -05:00
return std : : make_shared < cHTTPConnection > ( * this ) ;
2014-05-01 05:48:03 -04:00
}
2013-09-27 14:33:18 -04:00
}
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
}