2013-09-27 18:14:26 +02: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 19:34:46 +02:00
#include "HTTPConnection.h"
2013-09-28 19:30:25 +02:00
#include "HTTPFormParser.h"
2014-05-01 11:48:03 +02:00
#include "SslHTTPConnection.h"
2013-09-27 18:14:26 +02: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 21:28:41 +02:00
class cDebugCallbacks :
2013-10-04 13:07:57 +02:00
public cHTTPServer :: cCallbacks ,
protected cHTTPFormParser :: cCallbacks
2013-09-27 21:28:41 +02:00
{
virtual void OnRequestBegun ( cHTTPConnection & a_Connection , cHTTPRequest & a_Request ) override
{
2014-02-28 16:26:23 +01:00
UNUSED ( a_Connection );
2013-09-28 19:30:25 +02:00
if ( cHTTPFormParser :: HasFormData ( a_Request ))
{
2013-10-04 13:07:57 +02:00
a_Request . SetUserData ( new cHTTPFormParser ( a_Request , * this ));
2013-09-28 19:30:25 +02:00
}
2013-09-27 21:28:41 +02:00
}
2014-04-01 16:36:00 +02:00
virtual void OnRequestBody ( cHTTPConnection & a_Connection , cHTTPRequest & a_Request , const char * a_Data , size_t a_Size ) override
2013-09-27 21:28:41 +02:00
{
2014-02-28 16:26:23 +01:00
UNUSED ( a_Connection );
2013-09-28 19:30:25 +02:00
cHTTPFormParser * FormParser = ( cHTTPFormParser * )( a_Request . GetUserData ());
2014-10-20 21:55:07 +01:00
if ( FormParser != nullptr )
2013-09-28 19:30:25 +02:00
{
FormParser -> Parse ( a_Data , a_Size );
}
2013-09-27 21:28:41 +02:00
}
virtual void OnRequestFinished ( cHTTPConnection & a_Connection , cHTTPRequest & a_Request ) override
{
2013-09-28 19:30:25 +02:00
cHTTPFormParser * FormParser = ( cHTTPFormParser * )( a_Request . GetUserData ());
2014-10-20 21:55:07 +01:00
if ( FormParser != nullptr )
2013-09-28 19:30:25 +02: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 20:06:35 +02:00
return ;
2013-09-28 19:30:25 +02:00
}
2013-10-04 20:28:30 +02: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 21:28:41 +02:00
cHTTPResponse Resp ;
Resp . SetContentType ( "text/plain" );
a_Connection . Send ( Resp );
a_Connection . Send ( "Hello, world" );
}
2013-10-04 13:07:57 +02:00
virtual void OnFileStart ( cHTTPFormParser & a_Parser , const AString & a_FileName ) override
{
// TODO
}
2014-04-01 16:36:00 +02:00
virtual void OnFileData ( cHTTPFormParser & a_Parser , const char * a_Data , size_t a_Size ) override
2013-10-04 13:07:57 +02:00
{
// TODO
}
virtual void OnFileEnd ( cHTTPFormParser & a_Parser ) override
{
// TODO
}
2013-09-27 21:28:41 +02:00
} g_DebugCallbacks ;
2015-01-24 10:11:34 +01: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 22:15:34 +02:00
////////////////////////////////////////////////////////////////////////////////
2013-09-27 21:28:41 +02:00
// cHTTPServer:
2013-09-27 18:14:26 +02:00
cHTTPServer :: cHTTPServer ( void ) :
2014-10-20 21:55:07 +01:00
m_Callbacks ( nullptr )
2013-09-27 18:14:26 +02:00
{
2014-05-02 23:46:06 +02:00
}
cHTTPServer ::~ cHTTPServer ()
{
Stop ();
}
2015-01-24 10:11:34 +01:00
bool cHTTPServer :: Initialize ( void )
2014-05-02 23:46:06 +02:00
{
// Read the HTTPS cert + key:
2014-05-01 11:48:03 +02:00
AString CertFile = cFile :: ReadWholeFile ( "webadmin/httpscert.crt" );
AString KeyFile = cFile :: ReadWholeFile ( "webadmin/httpskey.pem" );
if ( ! CertFile . empty () && ! KeyFile . empty ())
{
2014-05-01 15:21:41 +02:00
m_Cert . reset ( new cX509Cert );
2014-05-01 11:48:03 +02:00
int res = m_Cert -> Parse ( CertFile . data (), CertFile . size ());
2014-05-06 21:43:31 +02:00
if ( res == 0 )
2014-05-01 11:48:03 +02:00
{
2014-05-01 15:21:41 +02:00
m_CertPrivKey . reset ( new cCryptoKey );
2014-05-01 11:48:03 +02:00
int res2 = m_CertPrivKey -> ParsePrivate ( KeyFile . data (), KeyFile . size (), "" );
if ( res2 != 0 )
{
// Reading the private key failed, reset the cert:
2014-05-02 23:46:06 +02:00
LOGWARNING ( "WebServer: Cannot read HTTPS certificate private key: -0x%x" , - res2 );
2014-05-01 11:48:03 +02:00
m_Cert . reset ();
}
}
else
{
2014-05-02 23:46:06 +02:00
LOGWARNING ( "WebServer: Cannot read HTTPS certificate: -0x%x" , - res );
2014-05-01 11:48:03 +02:00
}
}
2013-09-27 18:14:26 +02:00
2014-05-02 23:46:06 +02:00
// Notify the admin about the HTTPS / HTTP status
2014-10-20 21:55:07 +01:00
if ( m_Cert . get () == nullptr )
2014-05-02 23:46:06 +02:00
{
2014-09-10 18:47:59 +01: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 23:46:06 +02:00
}
else
{
LOGINFO ( "WebServer: The server is running in secure HTTPS mode." );
}
2013-09-27 21:28:41 +02:00
return true ;
}
2015-01-24 10:11:34 +01:00
bool cHTTPServer :: Start ( cCallbacks & a_Callbacks , const AStringVector & a_Ports )
2013-09-27 21:28:41 +02:00
{
m_Callbacks = & a_Callbacks ;
2015-01-24 10:11:34 +01:00
// Open up requested ports:
for ( auto port : a_Ports )
2013-09-27 18:14:26 +02:00
{
2015-01-25 16:25:15 +01:00
UInt16 PortNum ;
if ( ! StringToInteger ( port , PortNum ))
2015-01-24 10:11:34 +01: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 18:14:26 +02:00
}
2013-09-27 21:28:41 +02:00
void cHTTPServer :: Stop ( void )
{
2015-01-24 10:11:34 +01:00
for ( auto handle : m_ServerHandles )
2013-09-27 21:28:41 +02:00
{
2015-01-24 10:11:34 +01:00
handle -> Close ();
}
m_ServerHandles . clear ();
2013-09-27 21:28:41 +02:00
}
2015-01-24 10:11:34 +01:00
cTCPLink :: cCallbacksPtr cHTTPServer :: OnIncomingConnection ( const AString & a_RemoteIPAddress , UInt16 a_RemotePort )
2013-09-27 18:14:26 +02:00
{
2015-01-24 10:11:34 +01:00
UNUSED ( a_RemoteIPAddress );
UNUSED ( a_RemotePort );
2014-10-20 21:55:07 +01:00
if ( m_Cert . get () != nullptr )
2014-05-01 11:48:03 +02:00
{
2015-01-24 10:11:34 +01:00
return std :: make_shared < cSslHTTPConnection > ( * this , m_Cert , m_CertPrivKey );
2014-05-01 11:48:03 +02:00
}
else
{
2015-01-24 10:11:34 +01:00
return std :: make_shared < cHTTPConnection > ( * this );
2014-05-01 11:48:03 +02:00
}
2013-09-27 20:33:18 +02:00
}
2013-09-27 18:14:26 +02:00
void cHTTPServer :: NewRequest ( cHTTPConnection & a_Connection , cHTTPRequest & a_Request )
{
2013-09-27 21:28:41 +02:00
m_Callbacks -> OnRequestBegun ( a_Connection , a_Request );
2013-09-27 18:14:26 +02:00
}
2014-04-01 16:36:00 +02:00
void cHTTPServer :: RequestBody ( cHTTPConnection & a_Connection , cHTTPRequest & a_Request , const char * a_Data , size_t a_Size )
2013-09-27 18:14:26 +02:00
{
2013-09-27 21:28:41 +02:00
m_Callbacks -> OnRequestBody ( a_Connection , a_Request , a_Data , a_Size );
2013-09-27 18:14:26 +02:00
}
void cHTTPServer :: RequestFinished ( cHTTPConnection & a_Connection , cHTTPRequest & a_Request )
{
2013-09-27 21:28:41 +02:00
m_Callbacks -> OnRequestFinished ( a_Connection , a_Request );
a_Connection . AwaitNextRequest ();
2013-09-27 18:14:26 +02:00
}