Team Fortress 2 Source Code as on 22/4/2020
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.

710 lines
25 KiB

  1. //====== Copyright � 1996-2010, Valve Corporation, All rights reserved. =======
  2. //
  3. // Purpose: HTTP related enums and objects, stuff that both clients and server use should go here
  4. //
  5. //=============================================================================
  6. #include "stdafx.h"
  7. #include <time.h>
  8. #include "msgprotobuf.h"
  9. #include "rtime.h"
  10. // memdbgon must be the last include file in a .cpp file!!!
  11. #include "tier0/memdbgon.h"
  12. using namespace GCSDK;
  13. //-----------------------------------------------------------------------------
  14. // Purpose: Check if the given status code is one that by spec allows a body, or is
  15. // one that "MUST NOT" include an entity body
  16. //-----------------------------------------------------------------------------
  17. bool CHTTPUtil::BStatusCodeAllowsBody( EHTTPStatusCode eHTTPStatus )
  18. {
  19. switch( eHTTPStatus )
  20. {
  21. case k_EHTTPStatusCode100Continue:
  22. case k_EHTTPStatusCode101SwitchingProtocols:
  23. case k_EHTTPStatusCode204NoContent:
  24. case k_EHTTPStatusCode205ResetContent:
  25. case k_EHTTPStatusCode304NotModified:
  26. return false;
  27. default:
  28. break;
  29. }
  30. return true;
  31. }
  32. //-----------------------------------------------------------------------------
  33. // Purpose: Wrapper to ease use of Steam Web APIs
  34. //-----------------------------------------------------------------------------
  35. CSteamAPIRequest::CSteamAPIRequest( EHTTPMethod eMethod, const char *pchInterface, const char *pchMethod, int nVersion ) :
  36. CHTTPRequest( eMethod, "api.steampowered.com", CFmtStr( "/%s/%s/v%04d/", pchInterface, pchMethod, nVersion ) )
  37. {
  38. if ( k_EHTTPMethodPOST == eMethod )
  39. {
  40. SetPOSTParamString( "format", "vdf" );
  41. SetPOSTParamString( "key", GGCBase()->GetSteamAPIKey() );
  42. }
  43. else
  44. {
  45. SetGETParamString( "format", "vdf" );
  46. SetGETParamString( "key", GGCBase()->GetSteamAPIKey() );
  47. }
  48. EUniverse universe = GGCHost()->GetUniverse();
  49. if ( universe == k_EUniverseBeta )
  50. {
  51. SetHostname( "api-beta.steampowered.com" );
  52. }
  53. else if ( universe == k_EUniverseDev )
  54. {
  55. // Set this to your dev universe API endpoint if not local
  56. SetHostname( "localhost:8282" );
  57. }
  58. }
  59. CHTTPRequest::CHTTPRequest( CMsgHttpRequest* pProto )
  60. {
  61. Init( pProto );
  62. }
  63. //-----------------------------------------------------------------------------
  64. // Purpose: Construct a request object, it's invalid until data is setup
  65. //-----------------------------------------------------------------------------
  66. CHTTPRequest::CHTTPRequest()
  67. {
  68. Init( NULL );
  69. }
  70. //-----------------------------------------------------------------------------
  71. // Purpose: Common init code
  72. //-----------------------------------------------------------------------------
  73. void CHTTPRequest::Init( CMsgHttpRequest* pProto )
  74. {
  75. //see if we are wrapping a proto that already exists
  76. if( pProto )
  77. {
  78. m_pProto = pProto;
  79. m_bOwnProto = false;
  80. }
  81. else
  82. {
  83. m_pProto = new CMsgHttpRequest;
  84. m_bOwnProto = true;
  85. SetEHTTPMethod( k_EHTTPMethodInvalid );
  86. // Default URL
  87. SetURL( "/" );
  88. }
  89. }
  90. //-----------------------------------------------------------------------------
  91. // Purpose: Constructor
  92. //-----------------------------------------------------------------------------
  93. CHTTPRequest::CHTTPRequest( EHTTPMethod eMethod, const char *pchHost, const char *pchRelativeURL )
  94. {
  95. Init( NULL );
  96. SetEHTTPMethod( eMethod );
  97. SetHostname( pchHost );
  98. SetURL( pchRelativeURL );
  99. }
  100. //-----------------------------------------------------------------------------
  101. // Purpose: Constructor
  102. //-----------------------------------------------------------------------------
  103. CHTTPRequest::CHTTPRequest( EHTTPMethod eMethod, const char *pchAbsoluteURL )
  104. {
  105. Init( NULL );
  106. SetEHTTPMethod( eMethod );
  107. // We need to break the URL down into host + relativeURL
  108. const char *pchHost = Q_strstr( pchAbsoluteURL, "://" );
  109. if ( pchHost )
  110. {
  111. // Skip past protocol
  112. pchHost += 3;
  113. // Find the next /, that is the beginning of the actual URL
  114. const char *pchURL = Q_strstr( pchHost, "/");
  115. if ( !pchURL )
  116. {
  117. // No URL specified after host? just default to / then.
  118. SetURL( "/" );
  119. }
  120. else
  121. {
  122. // Ok, now we must remove the query string
  123. const char *pchQueryString = Q_strstr( pchURL, "?" );
  124. if ( !pchQueryString )
  125. {
  126. // No query string, full thing is the URL
  127. SetURL( pchURL );
  128. }
  129. else
  130. {
  131. // URL is everything before query string
  132. SetURLDirect( pchURL, pchQueryString - pchURL );
  133. Assert( *pchQueryString == '?' );
  134. pchQueryString++;
  135. if( *pchQueryString )
  136. {
  137. m_pProto->mutable_get_params()->Clear();
  138. CUtlVector<CMsgHttpRequest_QueryParam> vecParams;
  139. RetrieveURLEncodedData( pchQueryString, Q_strlen( pchQueryString ), vecParams );
  140. FOR_EACH_VEC( vecParams, i )
  141. {
  142. *m_pProto->add_get_params() = vecParams[i];
  143. }
  144. }
  145. }
  146. }
  147. // Is there a userinfo separator in the hostname portion? We don't support
  148. // username/password authentication in the URL, but we must still skip it.
  149. const char *pchUserinfoSep = strchr( pchHost, '@' );
  150. if ( pchUserinfoSep && ( !pchURL || pchUserinfoSep < pchURL ) )
  151. {
  152. pchHost = pchUserinfoSep + 1;
  153. }
  154. // If we found a URL only set upto that, otherwise set everything as host
  155. if ( pchURL )
  156. SetHostnameDirect( pchHost, pchURL-pchHost );
  157. else
  158. SetHostname( pchHost );
  159. }
  160. else
  161. {
  162. AssertMsg( false, "Bad absolute URL to CHTTPRequest constructor, must start with protocol://" );
  163. EmitError( SPEW_GC, "Bad absolute URL to CHTTPRequest constructor, must start with protocol://" );
  164. }
  165. }
  166. //-----------------------------------------------------------------------------
  167. // Purpose: Destructor
  168. //-----------------------------------------------------------------------------
  169. CHTTPRequest::~CHTTPRequest()
  170. {
  171. if( m_bOwnProto )
  172. delete m_pProto;
  173. }
  174. //-----------------------------------------------------------------------------
  175. // Purpose: Get the value of a GET parameter, this is case-insensitive by default.
  176. //-----------------------------------------------------------------------------
  177. const CMsgHttpRequest_QueryParam *CHTTPRequest::GetGETParam( const char *pchGetParamName, bool bMatchCase ) const
  178. {
  179. const uint32 nNumParams = m_pProto->get_params_size();
  180. for( uint32 nParam = 0; nParam < nNumParams; nParam++ )
  181. {
  182. const CMsgHttpRequest_QueryParam& param = m_pProto->get_params( nParam );
  183. if ( bMatchCase )
  184. {
  185. if ( Q_strcmp( param.name().c_str(), pchGetParamName ) == 0 )
  186. return &param;
  187. }
  188. else
  189. {
  190. if ( Q_stricmp( param.name().c_str(), pchGetParamName ) == 0 )
  191. return &param;
  192. }
  193. }
  194. return NULL;
  195. }
  196. //-----------------------------------------------------------------------------
  197. // Purpose: Get the value of a GET parameter, this is case-insensitive by default.
  198. //-----------------------------------------------------------------------------
  199. const char *CHTTPRequest::GetGETParamString( const char *pchGetParamName, const char *pchDefault, bool bMatchCase ) const
  200. {
  201. const CMsgHttpRequest_QueryParam *pParam = GetGETParam( pchGetParamName, bMatchCase );
  202. if ( !pParam )
  203. return pchDefault;
  204. return pParam->value().c_str();
  205. }
  206. //-----------------------------------------------------------------------------
  207. // Purpose: Get the value of a GET parameter, this is case-insensitive by default.
  208. //-----------------------------------------------------------------------------
  209. bool CHTTPRequest::GetGETParamBool( const char *pchGetParamName, bool bDefault, bool bMatchCase ) const
  210. {
  211. const CMsgHttpRequest_QueryParam *pParam = GetGETParam( pchGetParamName, bMatchCase );
  212. if ( !pParam )
  213. return bDefault;
  214. return ( Q_atoi( pParam->value().c_str() ) != 0);
  215. }
  216. //-----------------------------------------------------------------------------
  217. // Purpose: Get the value of a GET parameter, this is case-insensitive by default.
  218. //-----------------------------------------------------------------------------
  219. int32 CHTTPRequest::GetGETParamInt32( const char *pchGetParamName, int32 nDefault, bool bMatchCase ) const
  220. {
  221. const CMsgHttpRequest_QueryParam *pParam = GetGETParam( pchGetParamName, bMatchCase );
  222. if ( !pParam )
  223. return nDefault;
  224. return Q_atoi( pParam->value().c_str() );
  225. }
  226. //-----------------------------------------------------------------------------
  227. // Purpose: Get the value of a GET parameter, this is case-insensitive by default.
  228. //-----------------------------------------------------------------------------
  229. uint32 CHTTPRequest::GetGETParamUInt32( const char *pchGetParamName, uint32 unDefault, bool bMatchCase ) const
  230. {
  231. const CMsgHttpRequest_QueryParam *pParam = GetGETParam( pchGetParamName, bMatchCase );
  232. if ( !pParam )
  233. return unDefault;
  234. return (uint32)V_atoui64( pParam->value().c_str() );
  235. }
  236. //-----------------------------------------------------------------------------
  237. // Purpose: Get the value of a GET parameter, this is case-insensitive by default.
  238. //-----------------------------------------------------------------------------
  239. int64 CHTTPRequest::GetGETParamInt64( const char *pchGetParamName, int64 nDefault, bool bMatchCase ) const
  240. {
  241. const CMsgHttpRequest_QueryParam *pParam = GetGETParam( pchGetParamName, bMatchCase );
  242. if ( !pParam )
  243. return nDefault;
  244. return V_atoi64( pParam->value().c_str() );
  245. }
  246. //-----------------------------------------------------------------------------
  247. // Purpose: Get the value of a GET parameter, this is case-insensitive by default.
  248. //-----------------------------------------------------------------------------
  249. uint64 CHTTPRequest::GetGETParamUInt64( const char *pchGetParamName, uint64 unDefault, bool bMatchCase ) const
  250. {
  251. const CMsgHttpRequest_QueryParam *pParam = GetGETParam( pchGetParamName, bMatchCase );
  252. if ( !pParam )
  253. return unDefault;
  254. return V_atoui64( pParam->value().c_str() );
  255. }
  256. //-----------------------------------------------------------------------------
  257. // Purpose: Get the value of a GET parameter, this is case-insensitive by default.
  258. //-----------------------------------------------------------------------------
  259. float CHTTPRequest::GetGETParamFloat( const char *pchGetParamName, float fDefault, bool bMatchCase ) const
  260. {
  261. const CMsgHttpRequest_QueryParam *pParam = GetGETParam( pchGetParamName, bMatchCase );
  262. if ( !pParam )
  263. return fDefault;
  264. return Q_atof( pParam->value().c_str() );
  265. }
  266. //-----------------------------------------------------------------------------
  267. // Purpose: Get the value of a POST parameter, this is case-insensitive by default.
  268. //-----------------------------------------------------------------------------
  269. const CMsgHttpRequest_QueryParam *CHTTPRequest::GetPOSTParam( const char *pchPostParamName, bool bMatchCase ) const
  270. {
  271. const uint32 nNumParams = m_pProto->post_params_size();
  272. for( uint32 nParam = 0; nParam < nNumParams; nParam++ )
  273. {
  274. const CMsgHttpRequest_QueryParam& param = m_pProto->post_params( nParam );
  275. if ( bMatchCase )
  276. {
  277. if ( Q_strcmp( param.name().c_str(), pchPostParamName ) == 0 )
  278. return &param;
  279. }
  280. else
  281. {
  282. if ( Q_stricmp( param.name().c_str(), pchPostParamName ) == 0 )
  283. return &param;
  284. }
  285. }
  286. return NULL;
  287. }
  288. //-----------------------------------------------------------------------------
  289. // Purpose: Get the value of a POST parameter, this is case-insensitive by default.
  290. //-----------------------------------------------------------------------------
  291. const char *CHTTPRequest::GetPOSTParamString( const char *pchGetParamName, const char *pchDefault, bool bMatchCase ) const
  292. {
  293. const CMsgHttpRequest_QueryParam *pParam = GetPOSTParam( pchGetParamName, bMatchCase );
  294. if ( !pParam )
  295. return pchDefault;
  296. return pParam->value().c_str();
  297. }
  298. //-----------------------------------------------------------------------------
  299. // Purpose: Get the value of a POST parameter, this is case-insensitive by default.
  300. //-----------------------------------------------------------------------------
  301. bool CHTTPRequest::GetPOSTParamBool( const char *pchGetParamName, bool bDefault, bool bMatchCase ) const
  302. {
  303. const CMsgHttpRequest_QueryParam *pParam = GetPOSTParam( pchGetParamName, bMatchCase );
  304. if ( !pParam )
  305. return bDefault;
  306. return ( Q_atoi( pParam->value().c_str() ) != 0);
  307. }
  308. //-----------------------------------------------------------------------------
  309. // Purpose: Get the value of a POST parameter, this is case-insensitive by default.
  310. //-----------------------------------------------------------------------------
  311. int32 CHTTPRequest::GetPOSTParamInt32( const char *pchGetParamName, int32 nDefault, bool bMatchCase ) const
  312. {
  313. const CMsgHttpRequest_QueryParam *pParam = GetPOSTParam( pchGetParamName, bMatchCase );
  314. if ( !pParam )
  315. return nDefault;
  316. return Q_atoi( pParam->value().c_str() );
  317. }
  318. //-----------------------------------------------------------------------------
  319. // Purpose: Get the value of a POST parameter, this is case-insensitive by default.
  320. //-----------------------------------------------------------------------------
  321. uint32 CHTTPRequest::GetPOSTParamUInt32( const char *pchGetParamName, uint32 unDefault, bool bMatchCase ) const
  322. {
  323. const CMsgHttpRequest_QueryParam *pParam = GetPOSTParam( pchGetParamName, bMatchCase );
  324. if ( !pParam )
  325. return unDefault;
  326. return (uint32)V_atoui64( pParam->value().c_str() );
  327. }
  328. //-----------------------------------------------------------------------------
  329. // Purpose: Get the value of a POST parameter, this is case-insensitive by default.
  330. //-----------------------------------------------------------------------------
  331. int64 CHTTPRequest::GetPOSTParamInt64( const char *pchGetParamName, int64 nDefault, bool bMatchCase ) const
  332. {
  333. const CMsgHttpRequest_QueryParam *pParam = GetPOSTParam( pchGetParamName, bMatchCase );
  334. if ( !pParam )
  335. return nDefault;
  336. return V_atoi64( pParam->value().c_str() );
  337. }
  338. //-----------------------------------------------------------------------------
  339. // Purpose: Get the value of a POST parameter, this is case-insensitive by default.
  340. //-----------------------------------------------------------------------------
  341. uint64 CHTTPRequest::GetPOSTParamUInt64( const char *pchGetParamName, uint64 unDefault, bool bMatchCase ) const
  342. {
  343. const CMsgHttpRequest_QueryParam *pParam = GetPOSTParam( pchGetParamName, bMatchCase );
  344. if ( !pParam )
  345. return unDefault;
  346. return V_atoui64( pParam->value().c_str() );
  347. }
  348. //-----------------------------------------------------------------------------
  349. // Purpose: Get the value of a POST parameter, this is case-insensitive by default.
  350. //-----------------------------------------------------------------------------
  351. float CHTTPRequest::GetPOSTParamFloat( const char *pchGetParamName, float fDefault, bool bMatchCase ) const
  352. {
  353. const CMsgHttpRequest_QueryParam *pParam = GetPOSTParam( pchGetParamName, bMatchCase );
  354. if ( !pParam )
  355. return fDefault;
  356. return Q_atof( pParam->value().c_str() );
  357. }
  358. //-----------------------------------------------------------------------------
  359. // Purpose: Add a GET param to the request
  360. //-----------------------------------------------------------------------------
  361. void CHTTPRequest::SetGETParamRaw( const char *pchGetParamName, uint8 *pData, uint32 cubDataLen )
  362. {
  363. // See if it already exists, and overwrite then (case sensitive!)
  364. CMsgHttpRequest_QueryParam *pParam = const_cast< CMsgHttpRequest_QueryParam* >( GetGETParam( pchGetParamName, true ) );
  365. if ( !pParam )
  366. {
  367. // Add a new one then
  368. pParam = m_pProto->add_get_params();
  369. pParam->set_name( pchGetParamName );
  370. }
  371. pParam->set_value( pData, cubDataLen );
  372. }
  373. //-----------------------------------------------------------------------------
  374. // Purpose: Add a POST param to the request given a string for the name and value
  375. //-----------------------------------------------------------------------------
  376. void CHTTPRequest::SetPOSTParamRaw( const char *pchPostParamName, uint8 *pData, uint32 cubDataLen )
  377. {
  378. // See if it already exists, and overwrite then (case sensitive!)
  379. CMsgHttpRequest_QueryParam *pParam = const_cast< CMsgHttpRequest_QueryParam* >( GetPOSTParam( pchPostParamName, true ) );
  380. if ( !pParam )
  381. {
  382. // Add a new one then
  383. pParam = m_pProto->add_post_params();
  384. pParam->set_name( pchPostParamName );
  385. }
  386. pParam->set_value( pData, cubDataLen );
  387. }
  388. //-----------------------------------------------------------------------------
  389. // Fetch a request headers value by header name
  390. //-----------------------------------------------------------------------------
  391. const char * CHTTPRequest::GetRequestHeaderValue( const char *pchRequestHeaderName, const char *pchDefault ) const
  392. {
  393. const uint32 nNumHeaders = m_pProto->headers_size();
  394. for( uint32 nHeader = 0; nHeader < nNumHeaders; nHeader++ )
  395. {
  396. const CMsgHttpRequest_RequestHeader& header = m_pProto->headers( nHeader );
  397. if( Q_stricmp( header.name().c_str(), pchRequestHeaderName ) == 0 )
  398. {
  399. return header.value().c_str();
  400. }
  401. }
  402. return pchDefault;
  403. }
  404. //-----------------------------------------------------------------------------
  405. // Set a header field for the request
  406. //-----------------------------------------------------------------------------
  407. void CHTTPRequest::SetRequestHeaderValue( const char *pchHeaderName, const char *pchHeaderString )
  408. {
  409. const uint32 nNumHeaders = m_pProto->headers_size();
  410. for( uint32 nHeader = 0; nHeader < nNumHeaders; nHeader++ )
  411. {
  412. CMsgHttpRequest_RequestHeader* pHeader = m_pProto->mutable_headers( nHeader );
  413. if( Q_stricmp( pHeader->name().c_str(), pchHeaderName ) == 0 )
  414. {
  415. pHeader->set_value( pchHeaderString );
  416. return;
  417. }
  418. }
  419. CMsgHttpRequest_RequestHeader* pHeader = m_pProto->add_headers();
  420. pHeader->set_name( pchHeaderName );
  421. pHeader->set_value( pchHeaderString );
  422. }
  423. //-----------------------------------------------------------------------------
  424. // Purpose: Fetch a request header by header name and convert it to a time value
  425. // This is just a helpful wrapper of GetRequestHeaderValue that deals with
  426. // parsing the time value
  427. //-----------------------------------------------------------------------------
  428. RTime32 CHTTPRequest::GetRequestHeaderTimeValue( const char *pchRequestHeaderName, RTime32 rtDefault ) const
  429. {
  430. const char *pchValue = GetRequestHeaderValue( pchRequestHeaderName );
  431. if( !pchValue )
  432. return rtDefault;
  433. return CRTime::RTime32FromHTTPDateString( pchValue );
  434. }
  435. //-----------------------------------------------------------------------------
  436. // Purpose: Set the body data for the request
  437. //-----------------------------------------------------------------------------
  438. void CHTTPRequest::SetBodyData( const void *pubData, uint32 cubData )
  439. {
  440. m_pProto->set_body( pubData, cubData );
  441. }
  442. //-----------------------------------------------------------------------------
  443. // Purpose: Get data out of a query string
  444. //-----------------------------------------------------------------------------
  445. void CHTTPRequest::RetrieveURLEncodedData( const char *pchQueryString, int nQueryStrLen, CUtlVector<CMsgHttpRequest_QueryParam> &vecParams )
  446. {
  447. CUtlBuffer bufParamName( 0, 64 );
  448. CUtlBuffer bufParamValue( 0, 128 );
  449. // We shouldn't get passed the ? from a query string, but if we do just skip it and parse ok anyway
  450. if ( nQueryStrLen && pchQueryString[0] == '?' )
  451. {
  452. ++pchQueryString;
  453. --nQueryStrLen;
  454. }
  455. bool bInParamValue = false;
  456. int iTokenStart = 0;
  457. for( int i=0; i < nQueryStrLen; ++i )
  458. {
  459. if ( pchQueryString[i] == '=' && !bInParamValue )
  460. {
  461. // = switches to value from name, starts a new value token
  462. bufParamName.Put( &pchQueryString[iTokenStart], i - iTokenStart );
  463. bInParamValue = true;
  464. iTokenStart = i + 1;
  465. }
  466. else if ( pchQueryString[i] == '&' )
  467. {
  468. // & terminates a value and starts a new name token
  469. if ( bInParamValue )
  470. {
  471. bufParamValue.Put( &pchQueryString[iTokenStart], i - iTokenStart );
  472. int iIndex = vecParams.AddToTail();
  473. CMsgHttpRequest_QueryParam *pParam = &vecParams[iIndex];
  474. uint32 unNameLen = Q_URLDecode( (char*)bufParamName.Base(), bufParamName.TellPut(), (const char*)bufParamName.Base(), bufParamName.TellPut() );
  475. pParam->set_name( (const char*)bufParamName.Base(), unNameLen );
  476. uint32 unDataLen = (uint32)Q_URLDecode( (char*)bufParamValue.Base(), bufParamValue.TellPut(), (const char*)bufParamValue.Base(), bufParamValue.TellPut() );
  477. pParam->set_value( (const uint8*)bufParamValue.Base(), unDataLen );
  478. }
  479. bufParamName.Clear();
  480. bufParamValue.Clear();
  481. bInParamValue = false;
  482. iTokenStart = i+1;
  483. }
  484. }
  485. // Use any left over value from the end of the query string
  486. if ( bInParamValue )
  487. {
  488. bufParamValue.Put( &pchQueryString[iTokenStart], nQueryStrLen - iTokenStart );
  489. int iIndex = vecParams.AddToTail();
  490. CMsgHttpRequest_QueryParam *pParam = &vecParams[iIndex];
  491. uint32 unNameLen = Q_URLDecode( (char*)bufParamName.Base(), bufParamName.TellPut(), (const char*)bufParamName.Base(), bufParamName.TellPut() );
  492. pParam->set_name( (const char*)bufParamName.Base(), unNameLen );
  493. uint32 unDataLen = (uint32)Q_URLDecode( (char*)bufParamValue.Base(), bufParamValue.TellPut(), (const char*)bufParamValue.Base(), bufParamValue.TellPut() );
  494. pParam->set_value( (const uint8*)bufParamValue.Base(), unDataLen );
  495. }
  496. }
  497. //----------------------------------------------------------------------------
  498. // Purpose: Gets a singleton buffer pool for HTTP responses
  499. //----------------------------------------------------------------------------
  500. static GCConVar http_response_max_pool_size_mb( "http_response_max_pool_size_mb", "10", "Maximum size in bytes of the HTTP Response buffer pool" );
  501. static GCConVar http_response_init_buffer_size( "http_response_init_buffer_size", "65536", "Initial buffer size for buffers in the HTTP Response buffer pool" );
  502. /*static*/ CBufferPool &CHTTPResponse::GetBufferPool()
  503. {
  504. static CBufferPool s_bufferPool( "HTTP Response", http_response_max_pool_size_mb, http_response_init_buffer_size, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::CONTAINS_CRLF );
  505. return s_bufferPool;
  506. }
  507. //-----------------------------------------------------------------------------
  508. // Purpose: Construct a response object, it defaults to being a 500 internal server error response
  509. //-----------------------------------------------------------------------------
  510. CHTTPResponse::CHTTPResponse() :
  511. m_pbufBody( GetBufferPool().GetBuffer() ),
  512. m_eStatusCode( k_EHTTPStatusCode500InternalServerError )
  513. {
  514. m_pkvResponseHeaders = new KeyValues( "ResponseHeaders" );
  515. }
  516. //-----------------------------------------------------------------------------
  517. // Purpose: Destructor
  518. //-----------------------------------------------------------------------------
  519. CHTTPResponse::~CHTTPResponse()
  520. {
  521. GetBufferPool().ReturnBuffer( m_pbufBody );
  522. m_pbufBody = NULL;
  523. if ( m_pkvResponseHeaders )
  524. m_pkvResponseHeaders->deleteThis();
  525. m_pkvResponseHeaders = NULL;
  526. }
  527. //-----------------------------------------------------------------------------
  528. // Purpose: Sets the "expiration" response header to a given number of seconds
  529. // ahead or behind the current time. You can set the Expires header directly as well,
  530. // this is just a helper so you don't have to deal with time formatting.
  531. //-----------------------------------------------------------------------------
  532. void CHTTPResponse::SetExpirationHeaderDeltaFromNow( int32 nSecondsFromNow )
  533. {
  534. time_t rawtime;
  535. time( &rawtime );
  536. rawtime += nSecondsFromNow;
  537. SetHeaderTimeValue( "expires", rawtime );
  538. }
  539. //-----------------------------------------------------------------------------
  540. // Purpose: Formats a time value and sets it as a header. This is a helper so
  541. // you don't have to deal with time formatting
  542. //-----------------------------------------------------------------------------
  543. void CHTTPResponse::SetHeaderTimeValue( const char *pchHeaderName, RTime32 rtTimestamp )
  544. {
  545. char rgchDate[128];
  546. struct tm tmStruct;
  547. time_t rawtime = rtTimestamp;
  548. Plat_gmtime( &rawtime, &tmStruct );
  549. DbgVerify( strftime( rgchDate, 128, "%a, %d %b %Y %H:%M:%S GMT", &tmStruct ) );
  550. SetResponseHeaderValue( pchHeaderName, rgchDate );
  551. }
  552. //-----------------------------------------------------------------------------
  553. // Purpose: Serializes the response into a message object (for proxying between
  554. // back-end Steam servers).
  555. //-----------------------------------------------------------------------------
  556. void CHTTPResponse::SerializeIntoProtoBuf( CMsgHttpResponse & response ) const
  557. {
  558. MEM_ALLOC_CREDIT();
  559. response.set_status_code( m_eStatusCode );
  560. FOR_EACH_VALUE( m_pkvResponseHeaders, pkvRequestHeader )
  561. {
  562. const char *pchName = pkvRequestHeader->GetName();
  563. const char *pchValue = pkvRequestHeader->GetString();
  564. if ( pchName && pchValue )
  565. {
  566. CMsgHttpResponse_ResponseHeader *pHeader = response.add_headers();
  567. pHeader->set_name( pchName );
  568. pHeader->set_value( pchValue );
  569. }
  570. }
  571. if( m_pbufBody->TellPut() > 0 )
  572. response.set_body( m_pbufBody->Base(), m_pbufBody->TellPut() );
  573. }
  574. //-----------------------------------------------------------------------------
  575. // Purpose: Deserializes the response from a message object (for proxying between
  576. // back-end Steam servers).
  577. //-----------------------------------------------------------------------------
  578. void CHTTPResponse::DeserializeFromProtoBuf( const CMsgHttpResponse & response )
  579. {
  580. m_eStatusCode = (EHTTPStatusCode)response.status_code();
  581. for( int i=0; i<response.headers_size(); i++ )
  582. {
  583. m_pkvResponseHeaders->SetString( response.headers(i).name().c_str(), response.headers(i).value().c_str() );
  584. }
  585. if( response.has_body() )
  586. m_pbufBody->Put( response.body().data(), response.body().size() );
  587. }