You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
406 lines
11 KiB
406 lines
11 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
//=======================================================================================// |
|
|
|
#include "sv_publishtest.h" |
|
#include "spew.h" |
|
#include "replay/replayutils.h" |
|
#include "replaysystem.h" |
|
#include "sv_basejob.h" |
|
#include "sv_fileservercleanup.h" |
|
#include "tier1/convar.h" |
|
#include "sv_filepublish.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
//---------------------------------------------------------------------------------------- |
|
|
|
const char *g_pAcceptableFileserverProtocols[] = { "http", "https", NULL }; |
|
const char *g_pAcceptableOffloadProtocols[] = { "ftp", NULL }; |
|
|
|
//---------------------------------------------------------------------------------------- |
|
|
|
class CPublishTester |
|
{ |
|
public: |
|
CPublishTester(); |
|
~CPublishTester(); |
|
|
|
bool Go(); |
|
|
|
private: |
|
bool Test_Emptyness( const char *pDescription, const char *pStr, bool bPrintResult ); |
|
bool Test_Hostname( const char *pHostname, const char *pProtocolExample ); |
|
bool Test_Protocol( const char *pDescription, const char *pProtocol, const char **pAcceptableProtocols ); |
|
bool Test_Port( int nPort ); |
|
bool Test_Path( const char *pDescription, const char *pPath, bool bForwardSlashesAllowed, bool bBackslashesAllowed ); |
|
bool Test_LocalWebServerCVars(); |
|
bool Test_IO( const char *pFilename ); |
|
bool Test_FilePublish( const char *pFilename, bool bOffload ); |
|
bool Test_PublishedFileDelete( const char *pFullFilename, const char *pFilename, bool bOffload ); |
|
bool Test_WaitingForPlayersCVar(); |
|
void PrintBaseUrlWarning(); |
|
|
|
char *m_pGarbageBuffer; |
|
CBaseJob *m_pJob; |
|
CBaseJob *m_pCleanerJob; |
|
}; |
|
|
|
//---------------------------------------------------------------------------------------- |
|
|
|
#define GARBAGE_BUFFER_SIZE ( 1024 * 1000 ) |
|
|
|
CPublishTester::CPublishTester() |
|
: m_pGarbageBuffer( NULL ), |
|
m_pJob( NULL ), |
|
m_pCleanerJob( NULL ) |
|
{ |
|
m_pGarbageBuffer = new char[ GARBAGE_BUFFER_SIZE ]; |
|
} |
|
|
|
CPublishTester::~CPublishTester() |
|
{ |
|
delete [] m_pGarbageBuffer; |
|
|
|
if ( m_pJob ) |
|
{ |
|
m_pJob->Release(); |
|
} |
|
|
|
if ( m_pCleanerJob ) |
|
{ |
|
m_pCleanerJob->Release(); |
|
} |
|
} |
|
|
|
bool CPublishTester::Test_Hostname( const char *pHostname, const char *pProtocolExample ) |
|
{ |
|
if ( !Test_Emptyness( "Hostname", pHostname, false ) ) |
|
return false; |
|
|
|
if ( V_strstr( pHostname, "://" ) ) |
|
{ |
|
g_pBlockSpewer->PrintEventResult( false ); |
|
CFmtStr fmtError( "Should not contain a protocol (e.g: %s)!", pProtocolExample ); |
|
g_pBlockSpewer->PrintEventError( fmtError.Access() ); |
|
return false; |
|
} |
|
|
|
// Test IP lookup |
|
char szIP[16]; |
|
if ( !g_pEngine->NET_GetHostnameAsIP( pHostname, szIP, sizeof( szIP ) ) ) |
|
{ |
|
g_pBlockSpewer->PrintEventResult( false ); |
|
g_pBlockSpewer->PrintEventError( "DNS lookup failed!" ); |
|
return false; |
|
} |
|
|
|
g_pBlockSpewer->PrintEventResult( true ); |
|
g_pBlockSpewer->PrintEmptyLine(); |
|
|
|
return true; |
|
} |
|
|
|
bool CPublishTester::Test_Emptyness( const char *pDescription, const char *pStr, bool bPrintResult ) |
|
{ |
|
g_pBlockSpewer->PrintValue( pDescription, pStr ); |
|
g_pBlockSpewer->PrintEventStartMsg( "Validating" ); |
|
|
|
if ( V_strlen( pStr ) == 0 ) |
|
{ |
|
g_pBlockSpewer->PrintEventResult( false ); |
|
g_pBlockSpewer->PrintEventError( "Empty!" ); |
|
return false; |
|
} |
|
|
|
if ( bPrintResult ) |
|
{ |
|
g_pBlockSpewer->PrintEventResult( true ); |
|
g_pBlockSpewer->PrintEmptyLine(); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool CPublishTester::Test_Port( int nPort ) |
|
{ |
|
g_pBlockSpewer->PrintValue( "Port", Replay_va( "%i", nPort ) ); |
|
g_pBlockSpewer->PrintEventStartMsg( "Validating" ); |
|
|
|
if ( nPort < 0 || nPort > 65535 ) |
|
{ |
|
g_pBlockSpewer->PrintEventResult( false ); |
|
g_pBlockSpewer->PrintEventError( "Port must be between 0 and 65535." ); |
|
return false; |
|
} |
|
|
|
g_pBlockSpewer->PrintEventResult( true ); |
|
g_pBlockSpewer->PrintEmptyLine(); |
|
|
|
return true; |
|
} |
|
|
|
bool CPublishTester::Test_Path( const char *pDescription, const char *pPath, bool bForwardSlashesAllowed, bool bBackslashesAllowed ) |
|
{ |
|
g_pBlockSpewer->PrintValue( pDescription, pPath ); |
|
g_pBlockSpewer->PrintEventStartMsg( "Validating" ); |
|
if ( V_strlen( pPath ) == 0 ) |
|
{ |
|
g_pBlockSpewer->PrintEventResult( false ); |
|
g_pBlockSpewer->PrintEventError( "Empty path not allowed." ); |
|
return false; |
|
} |
|
|
|
if ( !bBackslashesAllowed && V_strstr( pPath, "\\" ) ) |
|
{ |
|
g_pBlockSpewer->PrintEventResult( false ); |
|
g_pBlockSpewer->PrintEventError( "Backslashes not allowed!" ); |
|
return false; |
|
} |
|
|
|
if ( !bForwardSlashesAllowed && V_strstr( pPath, "/" ) ) |
|
{ |
|
g_pBlockSpewer->PrintEventResult( false ); |
|
g_pBlockSpewer->PrintEventError( "Forward slashes not allowed!" ); |
|
return false; |
|
} |
|
|
|
if ( V_strstr( pPath, "//" ) || V_strstr( pPath, "\\\\" ) ) |
|
{ |
|
g_pBlockSpewer->PrintEventResult( false ); |
|
g_pBlockSpewer->PrintEventError( "Double slash detected!" ); |
|
return false; |
|
} |
|
|
|
if ( V_strstr( pPath, ".." ) ) |
|
{ |
|
g_pBlockSpewer->PrintEventResult( false ); |
|
g_pBlockSpewer->PrintEventError( "\"..\" not allowed!" ); |
|
return false; |
|
} |
|
|
|
g_pBlockSpewer->PrintEventResult( true ); |
|
g_pBlockSpewer->PrintEmptyLine(); |
|
|
|
return true; |
|
} |
|
|
|
bool CPublishTester::Test_Protocol( const char *pDescription, const char *pProtocol, const char **pAcceptableProtocols ) |
|
{ |
|
g_pBlockSpewer->PrintValue( pDescription, pProtocol ); |
|
g_pBlockSpewer->PrintEventStartMsg( "Validating" ); |
|
|
|
// Test to see if the input protocol is acceptable |
|
bool bProtocolOK = false; |
|
int i = 0; |
|
while ( pAcceptableProtocols[ i ] ) |
|
{ |
|
if ( V_strcmp( pAcceptableProtocols[ i++ ], pProtocol ) == 0 ) |
|
{ |
|
bProtocolOK = true; |
|
break; |
|
} |
|
} |
|
|
|
// Protocol allowed? |
|
if ( !bProtocolOK ) |
|
{ |
|
g_pBlockSpewer->PrintEventResult( false ); |
|
CFmtStr fmtError( "Must be one of the following (case-sensitive): " ); |
|
i = 0; |
|
while ( pAcceptableProtocols[ i ] ) |
|
fmtError.AppendFormat( "\"%s\" ", pAcceptableProtocols[ i++ ] ); |
|
g_pBlockSpewer->PrintEventError( fmtError.Access() ); |
|
return false; |
|
} |
|
|
|
g_pBlockSpewer->PrintEventResult( true ); |
|
g_pBlockSpewer->PrintEmptyLine(); |
|
|
|
return true; |
|
} |
|
|
|
|
|
bool CPublishTester::Test_LocalWebServerCVars() |
|
{ |
|
// NOTE: We use the raw cvar here as opposed to CServerReplayContext::GetLocalFileServerPath(), |
|
// which actually fixes slashes. If the cvar is using incorrect slashes here for the given OS, |
|
// this test will fail with a specific error message. |
|
extern ConVar replay_local_fileserver_path; |
|
if ( !Test_Path( "Path", replay_local_fileserver_path.GetString(), IsPosix(), IsWindows() ) ) |
|
return false; |
|
|
|
return true; |
|
} |
|
|
|
bool CPublishTester::Test_IO( const char *pFilename ) |
|
{ |
|
g_pBlockSpewer->PrintTestHeader( "Testing File I/O" ); |
|
|
|
// Print out temp directory so the context for this section is clear |
|
g_pBlockSpewer->PrintValue( "Temp path", SV_GetTmpDir() ); |
|
g_pBlockSpewer->PrintEmptyLine(); |
|
|
|
// Open the file |
|
FileHandle_t hTmpFile = g_pFullFileSystem->Open( pFilename, "wb+" ); |
|
g_pBlockSpewer->PrintEventStartMsg( "Opening temp file" ); |
|
if ( !hTmpFile ) |
|
{ |
|
g_pBlockSpewer->PrintEventResult( false ); |
|
return false; |
|
} |
|
g_pBlockSpewer->PrintEventResult( true ); |
|
|
|
// Write the file |
|
g_pBlockSpewer->PrintEventStartMsg( "Allocating test buffer" ); // Lie. |
|
if ( !m_pGarbageBuffer ) |
|
{ |
|
g_pBlockSpewer->PrintEventResult( false ); |
|
return false; |
|
} |
|
g_pBlockSpewer->PrintEventResult( true ); |
|
|
|
g_pBlockSpewer->PrintEventStartMsg( "Writing temp file" ); |
|
if ( g_pFullFileSystem->Write( m_pGarbageBuffer, GARBAGE_BUFFER_SIZE, hTmpFile ) != GARBAGE_BUFFER_SIZE ) |
|
{ |
|
g_pBlockSpewer->PrintEventResult( false ); |
|
return false; |
|
} |
|
g_pBlockSpewer->PrintEventResult( true ); |
|
|
|
// Close the file |
|
g_pFullFileSystem->Close( hTmpFile ); |
|
|
|
return true; |
|
} |
|
|
|
bool CPublishTester::Test_FilePublish( const char *pFilename, bool bOffload ) |
|
{ |
|
g_pBlockSpewer->PrintTestHeader( "Testing file publisher" ); |
|
g_pBlockSpewer->PrintValue( "Fileserver type", "Local Web server" ); |
|
|
|
if ( !Test_LocalWebServerCVars() ) |
|
return false; |
|
|
|
m_pJob = SV_CreateLocalPublishJob( pFilename ); |
|
|
|
g_pBlockSpewer->PrintEmptyLine(); |
|
|
|
// Run publish test |
|
if ( !m_pJob || !SV_RunJobToCompletion( m_pJob ) ) |
|
{ |
|
g_pBlockSpewer->PrintEventError( m_pJob->GetErrorStr() ); |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool CPublishTester::Test_PublishedFileDelete( const char *pFullFilename, const char *pFilename, bool bOffload ) |
|
{ |
|
g_pBlockSpewer->PrintTestHeader( "Testing fileserver delete" ); |
|
|
|
if ( bOffload ) |
|
{ |
|
// Delete the file from the tmp dir |
|
g_pFullFileSystem->RemoveFile( pFullFilename ); |
|
} |
|
|
|
m_pCleanerJob = SV_CreateDeleteFileJob(); |
|
IFileserverCleanerJob *pCleanerJobImp = SV_CastJobToIFileserverCleanerJob( m_pCleanerJob ); |
|
pCleanerJobImp->AddFileForDelete( pFilename ); |
|
if ( !m_pCleanerJob || !SV_RunJobToCompletion( m_pCleanerJob ) ) |
|
{ |
|
g_pBlockSpewer->PrintEventError( m_pCleanerJob->GetErrorStr() ); |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool CPublishTester::Test_WaitingForPlayersCVar() |
|
{ |
|
ConVarRef mp_waitingforplayers_cancel( "mp_waitingforplayers_cancel" ); |
|
if ( mp_waitingforplayers_cancel.IsValid() && mp_waitingforplayers_cancel.GetBool() ) |
|
{ |
|
g_pBlockSpewer->PrintEventError( "mp_waitingforplayers_cancel must be 0 in order for replay to work!" ); |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
void CPublishTester::PrintBaseUrlWarning() |
|
{ |
|
g_pBlockSpewer->PrintEmptyLine(); |
|
g_pBlockSpewer->PrintMsg( "If clients can't access the following URL via a Web" ); |
|
g_pBlockSpewer->PrintMsg( "browser, they will not be able to download Replays." ); |
|
g_pBlockSpewer->PrintEmptyLine(); |
|
g_pBlockSpewer->PrintValue( "URL", Replay_GetDownloadURL() ); |
|
} |
|
|
|
bool CPublishTester::Go() |
|
{ |
|
const bool bOffload = false; |
|
|
|
// Force anyone outside of Go() to use the block spewer until we're done. |
|
CSpewScope SpewScope( g_pBlockSpewer ); |
|
|
|
g_pBlockSpewer->PrintMsg( "TESTING REPLAY SYSTEM CONFIGURATION..." ); |
|
|
|
// Fileserver convars |
|
g_pBlockSpewer->PrintTestHeader( "Testing Fileserver ConVars (replay_fileserver_*)" ); |
|
|
|
// Test replay_fileserver_protocol |
|
extern ConVar replay_fileserver_protocol; |
|
if ( !Test_Protocol( "Protocol", replay_fileserver_protocol.GetString(), g_pAcceptableFileserverProtocols ) ) |
|
return false; |
|
|
|
extern ConVar replay_fileserver_host; |
|
if ( !Test_Hostname( replay_fileserver_host.GetString(), "\"http\" or \"https\"" ) ) |
|
return false; |
|
|
|
extern ConVar replay_fileserver_port; |
|
if ( !Test_Port( replay_fileserver_port.GetInt() ) ) |
|
return false; |
|
|
|
extern ConVar replay_fileserver_path; |
|
if ( !Test_Path( "Path", replay_fileserver_path.GetString(), true, false ) ) |
|
return false; |
|
|
|
// Print out the base URL / warning |
|
PrintBaseUrlWarning(); |
|
|
|
CFmtStr fmtTmpFilename( "testpublish_%i.tmp", (int)abs( rand() % 10000 ) ); |
|
CFmtStr fmtTmpFilenameFullPath( "%s%s", SV_GetTmpDir(), fmtTmpFilename.Access() ); |
|
const char *pFilename = fmtTmpFilenameFullPath.Access(); |
|
|
|
// Test file I/O |
|
if ( !Test_IO( pFilename ) ) |
|
return false; |
|
|
|
// Get out if necessary |
|
if ( !Test_FilePublish( pFilename, bOffload ) ) |
|
return false; |
|
|
|
// Test delete from fileserver |
|
if ( !Test_PublishedFileDelete( pFilename, fmtTmpFilename.Access(), bOffload ) ) |
|
return false; |
|
|
|
// Make sure mp_waitingforplayers_cancel isn't on or replay will be fucked. |
|
if ( !Test_WaitingForPlayersCVar() ) |
|
return false; |
|
|
|
return true; |
|
} |
|
|
|
//---------------------------------------------------------------------------------------- |
|
|
|
bool SV_DoTestPublish() |
|
{ |
|
CPublishTester tester; |
|
return tester.Go(); |
|
} |
|
|
|
//----------------------------------------------------------------------------------------
|
|
|