//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //============================================================================= #ifndef GCJOB_H #define GCJOB_H #ifdef _WIN32 #pragma once #endif #include "gcwebapikey.h" namespace GCSDK { //a free standing utility function that takes in a newly allocated job, and tells the job to start executing. An example of this would be pStore = StartNewJobDelayed( new CFooJob( Params ) ); template < typename T > inline typename T* StartNewJobDelayed( T* pNewJob ) { if ( pNewJob ) pNewJob->StartJobDelayed( NULL ); return pNewJob; } //----------------------------------------------------------------------------- // Purpose: Control access to web api accounts so that they can be rate limited/blocked/exempt, etc //----------------------------------------------------------------------------- enum EWebAPIAccountLevel { eWebAPIAccountLevel_RateLimited = 0, //default, rate limit eWebAPIAccountLevel_Blocked = 1, //block all requests eWebAPIAccountLevel_Unlimited = 2, //do not rate limit eWebAPIAccountLevel_Elevated = 3, //like rate limiting, but at a higher threshold }; //resets all accounts to the default permission void WebAPIAccount_ResetAllPermissions(); //called to associate a permission level with an account void WebAPIAccount_SetPermission( AccountID_t nID, EWebAPIAccountLevel eLevel ); //external calling interface in case we want to use this from a non-WebAPI job bool WebAPIAccount_BTrackUserAndValidate( AccountID_t nID, uint32 unIP ); //----------------------------------------------------------------------------- // Purpose: handles a network message job from the client //----------------------------------------------------------------------------- class CGCJob : public CJob { public: // Constructor: when overriding job name a static string pointer must be used CGCJob( CGCBase *pGC, const char *pchJobName = NULL ) : CJob( pGC->GetJobMgr(), pchJobName ), m_pGC( pGC ), m_cHeartbeatsBeforeTimeout( k_cJobHeartbeatsBeforeTimeoutDefault ) {} // all GC jobs must implement one of these virtual bool BYieldingRunGCJob( IMsgNetPacket *pNetPacket ) { return false; } virtual bool BYieldingRunGCJob() { return false; } virtual EServerType GetServerType() { return k_EServerTypeGC; } bool BYldSendMessageAndGetReply( CSteamID &steamIDTarget, CGCMsgBase &msgOut, uint nTimeoutSec, CGCMsgBase *pMsgIn, MsgType_t eMsg ); bool BYldSendMessageAndGetReply( CSteamID &steamIDTarget, CGCMsgBase &msgOut, uint nTimeoutSec, IMsgNetPacket **ppNetPacket ); bool BYldSendMessageAndGetReply( CSteamID &steamIDTarget, CProtoBufMsgBase &msgOut, uint nTimeoutSec, CProtoBufMsgBase *pMsgIn, MsgType_t eMsg ); bool BYldSendMessageAndGetReply( CSteamID &steamIDTarget, CProtoBufMsgBase &msgOut, uint nTimeoutSec, IMsgNetPacket **ppNetPacket ); virtual uint32 CHeartbeatsBeforeTimeout() { return m_cHeartbeatsBeforeTimeout; } void SetJobTimeout( uint nTimeoutSec ) { m_cHeartbeatsBeforeTimeout = 1 + ( ( nTimeoutSec * k_nMillion - 1 ) / k_cMicroSecJobHeartbeat ); } protected: CGCBase *m_pGC; private: virtual bool BYieldingRunJobFromMsg( IMsgNetPacket *pNetPacket ) { return BYieldingRunGCJob( pNetPacket ); } virtual bool BYieldingRunJob( void *pvStartParam ) { return BYieldingRunGCJob(); } uint32 m_cHeartbeatsBeforeTimeout; }; //This template class is designed to be derived from various job types and provides an adapter for the run from message hook, and handles //converting it over to an appropriate protobuf message for the caller as well as validating that it parsed properly. It will bail on processing //the message if the protobuf does not parse. template < typename TGCJobClass, typename TGCType, typename TProtoMsgClass > class TProtoMsgJob : public TGCJobClass { public: typedef CProtoBufMsg< TProtoMsgClass > TProtoMsg; TProtoMsgJob( TGCType* pGC ) : TGCJobClass( pGC ) {} virtual bool BYieldingRunJobFromMsg( IMsgNetPacket *pNetPacket ) OVERRIDE { TProtoMsg msg( pNetPacket ); if ( !msg.Body().IsInitialized() ) return false; return BYieldingRunJobFromProtoMsg( msg ); } virtual bool BYieldingRunJobFromProtoMsg( const TProtoMsg& ProtoMsg ) = 0; }; //a partial specialization of the proto message job to make it easier to use with GCJob template < typename TProtoMsgClass > class CGCProtoJob : public TProtoMsgJob < CGCJob, CGCBase, TProtoMsgClass > { public: CGCProtoJob( CGCBase* pGC ) : TProtoMsgJob( pGC ) {} //clients need to implement BYieldingRunJobFromProtoMsg }; //----------------------------------------------------------------------------- // CGCWGJob - A job invoked from forwarded WG messages //----------------------------------------------------------------------------- class CGCWGJob : public CGCJob { public: CGCWGJob( CGCBase *pGCBase ); ~CGCWGJob(); bool BYieldingRunGCJob( IMsgNetPacket * pNetPacket ); // invokes BYieldingRunJobFromRequest virtual bool BYieldingRunJobFromRequest( KeyValues *pkvRequest, KeyValues *pkvResponse ) = 0; virtual bool BVerifyParams( const CGCMsg & msg, KeyValues *pkvRequest, const WebApiFunc_t * pWebApiFunc ); void SetWebApiFunc( const WebApiFunc_t * pWebApiFunc ) { m_pWebApiFunc = pWebApiFunc; } void SetErrorMessage( KeyValues *pkvErr, const char *pchErrorMsg, int32 nResult ); protected: KeyValues *m_pkvResponse; CUtlBuffer m_bufRequest; // packed binary form of the request const WebApiFunc_t * m_pWebApiFunc; // requester auth info, like WGRequestContext CSteamID m_steamID; }; //----------------------------------------------------------------------------- // CGCJobVerifySession - A job that asks steam if a given user is still connected // and cleans up the session if the user is gone //----------------------------------------------------------------------------- class CGCJobVerifySession : public CGCJob { public: CGCJobVerifySession( CGCBase *pGC, const CSteamID &steamID ) : CGCJob( pGC ), m_steamID( steamID ) { } virtual bool BYieldingRunGCJob(); private: CSteamID m_steamID; }; //----------------------------------------------------------------------------- // CWebAPIJob - A job invoked from a forwarded WebAPI request //----------------------------------------------------------------------------- class CWebAPIJob : public CGCJob { public: CWebAPIJob( CGCBase *pGC, EWebAPIOutputFormat eDefaultOutputFormat = k_EWebAPIOutputFormat_JSON ); ~CWebAPIJob(); // Called by jobmgr, and then invokes appropriate run function for specific API version requested bool BYieldingRunJobFromMsg( IMsgNetPacket * pNetPacket ); // Implemented by each individual WebAPI job, should be declared using BEGIN_WEBAPI_JOB_VERSION_ADAPTERS and related macros. virtual bool BYieldingRunJobFromAPIRequest( const char *pchInterface, const char *pchMethod, uint32 unVersion, CHTTPRequest *pRequest, CHTTPResponse *pResponse, CWebAPIResponse *pWebAPIResponse ) = 0; protected: static void AddLocalizedString( CWebAPIValues *pOutDefn, const char *pchFieldName, const char *pchKeyName, ELanguage eLang, bool bReturnTokenIfNotFound = true ); static void ThreadedEmitFormattedOutputWrapper( CWebAPIResponse *pResponse, EWebAPIOutputFormat eFormat, CUtlBuffer *poutputBuffer, size_t unMaxResultSize, bool *pbResult ); CWebAPIKey m_webAPIKey; EWebAPIOutputFormat m_eDefaultOutputFormat; }; #define BEGIN_WEBAPI_JOB_VERSION_ADAPTERS( interface, method ) \ bool BYieldingRunJobFromAPIRequest( const char *pchInterface, const char *pchMethod, uint32 unVersion, CHTTPRequest *pRequest, CHTTPResponse *pResponse, CWebAPIResponse *pWebAPIResponse ) \ { \ if ( Q_strnicmp( pchInterface, interface, Q_strlen( interface ) ) != 0 ) \ { \ AssertMsg2( false, "WebAPIJob recieved request for unexpected interface (got %s, expected %s)!", pchInterface, interface ); \ pResponse->SetStatusCode( k_EHTTPStatusCode500InternalServerError ); \ return false; \ } \ \ if ( Q_stricmp( pchMethod, method ) != 0 ) \ { \ AssertMsg2( false, "WebAPIJob received request fo unexpected method (got %s, expected %s)!", pchMethod, method ); \ pResponse->SetStatusCode( k_EHTTPStatusCode500InternalServerError ); \ return false; \ } \ \ \ bool bFoundVersion = false; \ bool bResult = false; \ switch( unVersion ) \ { #define WEBAPI_JOB_VERSION_ADAPTER( version, funcname ) \ case version: \ bFoundVersion = true; \ { \ VPROF_BUDGET( #funcname, VPROF_BUDGETGROUP_JOBS_COROUTINES ); \ bResult = this->##funcname( pRequest, pWebAPIResponse ); \ } \ break; #define WEBAPI_JOB_VERSION_ADAPTER_EXTENDED_ARRAYS( version, funcname ) \ case version: \ bFoundVersion = true; \ pWebAPIResponse->SetExtendedArrays( true ); \ { \ VPROF_BUDGET( #funcname, VPROF_BUDGETGROUP_JOBS_COROUTINES ); \ bResult = this->##funcname( pRequest, pWebAPIResponse ); \ } \ break; #define END_WEBAPI_JOB_VERSION_ADAPTERS() \ default: \ break; \ } \ AssertMsg3( bFoundVersion, "WebAPIJob for %s/%s received unhandled version %d", pchInterface, pchMethod, unVersion ); \ return bResult; \ } } // namespace GCSDK #endif // GCJOB_H