main.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. /*
  2. * Copyright (c) 2014, Oculus VR, Inc.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. *
  9. */
  10. /// \file
  11. /// \brief This file is a sample for using HTTPConnection and PHPDirectoryServer2
  12. #include "TCPInterface.h"
  13. #include "HTTPConnection.h"
  14. #include "PHPDirectoryServer2.h"
  15. #include "RakSleep.h"
  16. #include "RakString.h"
  17. #include "GetTime.h"
  18. #include "DS_Table.h"
  19. #include <cstring>
  20. #include <cstdlib>
  21. #include <cstdio>
  22. #include "Gets.h"
  23. #include "Getche.h"
  24. using namespace RakNet;
  25. // Allocate rather than create on the stack or the RakString mutex crashes on shutdown
  26. TCPInterface *tcp;
  27. HTTPConnection *httpConnection;
  28. PHPDirectoryServer2 *phpDirectoryServer2;
  29. enum ReadResultEnum
  30. {
  31. RR_EMPTY_TABLE,
  32. RR_READ_TABLE,
  33. RR_TIMEOUT,
  34. };
  35. ReadResultEnum ReadResult(RakNet::RakString &httpResult)
  36. {
  37. RakNet::TimeMS endTime=RakNet::GetTimeMS()+10000;
  38. httpResult.Clear();
  39. while (RakNet::GetTimeMS()<endTime)
  40. {
  41. Packet *packet = tcp->Receive();
  42. if(packet)
  43. {
  44. httpConnection->ProcessTCPPacket(packet);
  45. tcp->DeallocatePacket(packet);
  46. }
  47. if (httpConnection->HasRead())
  48. {
  49. httpResult = httpConnection->Read();
  50. // Good response, let the PHPDirectoryServer2 class handle the data
  51. // If resultCode is not an empty string, then we got something other than a table
  52. // (such as delete row success notification, or the message is for HTTP only and not for this class).
  53. HTTPReadResult readResult = phpDirectoryServer2->ProcessHTTPRead(httpResult);
  54. if (readResult==HTTP_RESULT_GOT_TABLE)
  55. {
  56. //printf("RR_READ_TABLE\n");
  57. return RR_READ_TABLE;
  58. }
  59. else if (readResult==HTTP_RESULT_EMPTY)
  60. {
  61. //printf("HTTP_RESULT_EMPTY\n");
  62. return RR_EMPTY_TABLE;
  63. }
  64. }
  65. // Update our two classes so they can do time-based updates
  66. httpConnection->Update();
  67. phpDirectoryServer2->Update();
  68. // Prevent 100% cpu usage
  69. RakSleep(30);
  70. }
  71. return RR_TIMEOUT;
  72. }
  73. bool HaltOnUnexpectedResult(ReadResultEnum result, ReadResultEnum expected)
  74. {
  75. if (result!=expected)
  76. {
  77. printf("TEST FAILED. Expected ");
  78. switch (expected)
  79. {
  80. case RR_EMPTY_TABLE:
  81. printf("no results");
  82. break;
  83. case RR_TIMEOUT:
  84. printf("timeout");
  85. break;
  86. case RR_READ_TABLE:
  87. printf("to download result");
  88. break;
  89. }
  90. switch (result)
  91. {
  92. case RR_EMPTY_TABLE:
  93. printf(". No results were downloaded");
  94. break;
  95. case RR_READ_TABLE:
  96. printf(". Got a result");
  97. break;
  98. case RR_TIMEOUT:
  99. printf(". Timeout");
  100. break;
  101. }
  102. printf("\n");
  103. return true;
  104. }
  105. return false;
  106. }
  107. void DownloadTable()
  108. {
  109. phpDirectoryServer2->DownloadTable("a");
  110. }
  111. void UploadTable(RakNet::RakString gameName, unsigned short gamePort)
  112. {
  113. phpDirectoryServer2->UploadTable("a", gameName, gamePort, false);
  114. }
  115. void UploadAndDownloadTable(RakNet::RakString gameName, unsigned short gamePort)
  116. {
  117. phpDirectoryServer2->UploadAndDownloadTable("a", "a", gameName, gamePort, false);
  118. }
  119. bool PassTestOnEmptyDownloadedTable()
  120. {
  121. const DataStructures::Table *games = phpDirectoryServer2->GetLastDownloadedTable();
  122. if (games->GetRowCount()==0)
  123. {
  124. printf("Test passed.\n");
  125. return true;
  126. }
  127. printf("TEST FAILED. Empty table should have been downloaded.\n");
  128. return false;
  129. }
  130. bool VerifyDownloadMatchesUpload(int requiredRowCount, int testRowIndex)
  131. {
  132. const DataStructures::Table *games = phpDirectoryServer2->GetLastDownloadedTable();
  133. if (games->GetRowCount()!=(unsigned int)requiredRowCount)
  134. {
  135. printf("TEST FAILED. Expected %i result rows, got %i\n", requiredRowCount, games->GetRowCount());
  136. return false;
  137. }
  138. RakNet::RakString columnName;
  139. RakNet::RakString value;
  140. unsigned int i;
  141. DataStructures::Table::Row *row = games->GetRowByIndex(testRowIndex,NULL);
  142. const DataStructures::List<DataStructures::Table::ColumnDescriptor>& columns = games->GetColumns();
  143. unsigned int colIndex;
  144. // +4 comes from automatic fields
  145. // _GAME_PORT
  146. // _GAME_NAME
  147. // _SYSTEM_ADDRESS
  148. // __SEC_AFTER_EPOCH_SINCE_LAST_UPDATE
  149. if (phpDirectoryServer2->GetFieldCount()+4!=games->GetColumnCount())
  150. {
  151. printf("TEST FAILED. Expected %i columns, got %i\n", phpDirectoryServer2->GetFieldCount()+4, games->GetColumnCount());
  152. printf("Expected columns:\n");
  153. for (colIndex=0; colIndex < phpDirectoryServer2->GetFieldCount(); colIndex++)
  154. {
  155. phpDirectoryServer2->GetField(colIndex, columnName, value);
  156. printf("%i. %s\n", colIndex+1, columnName.C_String());
  157. }
  158. printf("%i. _GAME_PORT\n", colIndex++);
  159. printf("%i. _GAME_NAME\n", colIndex++);
  160. printf("%i. _System_Address\n", colIndex++);
  161. printf("%i. __SEC_AFTER_EPOCH_SINCE_LAST_UPDATE\n", colIndex++);
  162. printf("Got columns:\n");
  163. for (colIndex=0; colIndex < columns.Size(); colIndex++)
  164. {
  165. printf("%i. %s\n", colIndex+1, columns[colIndex].columnName);
  166. }
  167. return false;
  168. }
  169. for (i=0; i < phpDirectoryServer2->GetFieldCount(); i++)
  170. {
  171. phpDirectoryServer2->GetField(i, columnName, value);
  172. for (colIndex=0; colIndex < columns.Size(); colIndex++)
  173. {
  174. if (strcmp(columnName.C_String(), columns[colIndex].columnName)==0)
  175. break;
  176. }
  177. if (colIndex==columns.Size())
  178. {
  179. printf("TEST FAILED. Expected column with name %s\n", columnName.C_String());
  180. return false;
  181. }
  182. if (strcmp(value.C_String(), row->cells[colIndex]->c)!=0)
  183. {
  184. printf("TEST FAILED. Expected row with value '%s' at index %i for column %s. Got '%s'.\n", value.C_String(), i, columnName.C_String(), row->cells[colIndex]->c);
  185. return false;
  186. }
  187. }
  188. printf("Test passed.\n");
  189. return true;
  190. }
  191. void PrintHttpResult(RakNet::RakString httpResult)
  192. {
  193. printf("--- Last result read ---\n");
  194. printf("%s", httpResult.C_String());
  195. }
  196. void PrintFieldColumns(void)
  197. {
  198. unsigned int colIndex;
  199. RakNet::RakString columnName;
  200. RakNet::RakString value;
  201. for (colIndex=0; colIndex < phpDirectoryServer2->GetFieldCount(); colIndex++)
  202. {
  203. phpDirectoryServer2->GetField(colIndex, columnName, value);
  204. printf("%i. %s\n", colIndex+1, columnName.C_String());
  205. }
  206. }
  207. bool RunTest()
  208. {
  209. RakNet::RakString httpResult;
  210. ReadResultEnum rr;
  211. char ch[32];
  212. printf("Warning, table must be clear before starting the test.\n");
  213. printf("Press enter to start\n");
  214. Gets(ch,sizeof(ch));
  215. printf("*** Testing initial table is empty.\n");
  216. // Table should start emptyF
  217. DownloadTable();
  218. if (HaltOnUnexpectedResult(rr=ReadResult(httpResult), RR_EMPTY_TABLE))
  219. {PrintHttpResult(httpResult); return false;}
  220. if (PassTestOnEmptyDownloadedTable()==false)
  221. {PrintHttpResult(httpResult); return false;}
  222. printf("*** Downloading again, to ensure download does not modify the table.\n");
  223. // Downloading should not modify the table
  224. DownloadTable();
  225. if (HaltOnUnexpectedResult(rr=ReadResult(httpResult), RR_EMPTY_TABLE))
  226. {PrintHttpResult(httpResult); return false;}
  227. if (PassTestOnEmptyDownloadedTable()==false)
  228. {PrintHttpResult(httpResult); return false;}
  229. printf("*** Testing upload.\n");
  230. // Upload values likely to mess up PHP
  231. phpDirectoryServer2->SetField("TestField1","0");
  232. phpDirectoryServer2->SetField("TestField2","");
  233. phpDirectoryServer2->SetField("TestField3"," ");
  234. phpDirectoryServer2->SetField("TestField4","!@#$%^&*(");
  235. phpDirectoryServer2->SetField("TestField5","A somewhat big long string as these things typically go.\nIt even has a linebreak!");
  236. phpDirectoryServer2->SetField("TestField6","=");
  237. phpDirectoryServer2->UploadTable("a", "FirstGameUpload", 80, false);
  238. if (HaltOnUnexpectedResult(rr=ReadResult(httpResult), RR_EMPTY_TABLE))
  239. {PrintHttpResult(httpResult); return false;}
  240. if (PassTestOnEmptyDownloadedTable()==false)
  241. {PrintHttpResult(httpResult); return false;}
  242. printf("*** Testing download, should match upload exactly.\n");
  243. // Download what we just uploaded
  244. DownloadTable();
  245. if (HaltOnUnexpectedResult(rr=ReadResult(httpResult), RR_READ_TABLE))
  246. {PrintHttpResult(httpResult); return false;}
  247. // Check results
  248. if (VerifyDownloadMatchesUpload(1,0)==false)
  249. {PrintHttpResult(httpResult); return false;}
  250. printf("*** Testing that download works twice in a row.\n");
  251. // Make sure download works twice
  252. DownloadTable();
  253. if (HaltOnUnexpectedResult(rr=ReadResult(httpResult), RR_READ_TABLE))
  254. {PrintHttpResult(httpResult); return false;}
  255. // Check results
  256. if (VerifyDownloadMatchesUpload(1,0)==false)
  257. {PrintHttpResult(httpResult); return false;}
  258. printf("*** Testing reuploading a game to modify fields.\n");
  259. // Modify fields
  260. phpDirectoryServer2->SetField("TestField1","zero");
  261. phpDirectoryServer2->SetField("TestField2","empty");
  262. phpDirectoryServer2->SetField("TestField3","space");
  263. phpDirectoryServer2->SetField("TestField4","characters");
  264. phpDirectoryServer2->SetField("TestField5","A shorter string");
  265. phpDirectoryServer2->SetField("TestField6","Test field 6");
  266. phpDirectoryServer2->UploadTable("a", "FirstGameUpload", 80, false);
  267. if (HaltOnUnexpectedResult(rr=ReadResult(httpResult), RR_EMPTY_TABLE))
  268. {PrintHttpResult(httpResult); return false;}
  269. if (PassTestOnEmptyDownloadedTable()==false)
  270. {PrintHttpResult(httpResult); return false;}
  271. printf("*** Testing that downloading returns modified fields.\n");
  272. // Download what we just uploaded
  273. DownloadTable();
  274. if (HaltOnUnexpectedResult(rr=ReadResult(httpResult), RR_READ_TABLE))
  275. {PrintHttpResult(httpResult); return false;}
  276. // Check results
  277. if (VerifyDownloadMatchesUpload(1,0)==false)
  278. {PrintHttpResult(httpResult); return false;}
  279. printf("*** Testing that downloading works twice.\n");
  280. // Make sure download works twice
  281. DownloadTable();
  282. if (HaltOnUnexpectedResult(rr=ReadResult(httpResult), RR_READ_TABLE))
  283. {PrintHttpResult(httpResult); return false;}
  284. // Check results
  285. if (VerifyDownloadMatchesUpload(1,0)==false)
  286. {PrintHttpResult(httpResult); return false;}
  287. printf("*** Testing upload of a second game.\n");
  288. // Upload another game
  289. phpDirectoryServer2->SetField("TestField1","0");
  290. phpDirectoryServer2->SetField("TestField2","");
  291. phpDirectoryServer2->SetField("TestField3"," ");
  292. phpDirectoryServer2->SetField("TestField4","Game two characters !@#$%^&*(");
  293. phpDirectoryServer2->UploadTable("a", "SecondGameUpload", 80, false);
  294. if (HaltOnUnexpectedResult(rr=ReadResult(httpResult), RR_EMPTY_TABLE))
  295. {PrintHttpResult(httpResult); return false;}
  296. if (PassTestOnEmptyDownloadedTable()==false)
  297. {PrintHttpResult(httpResult); return false;}
  298. RakNet::TimeMS startTime = RakNet::GetTimeMS();
  299. printf("*** Testing 20 repeated downloads.\n");
  300. //printf("Field columns\n");
  301. //PrintFieldColumns();
  302. // Download repeatedly
  303. unsigned int downloadCount=0;
  304. while (downloadCount < 20)
  305. {
  306. printf("*** (%i) Downloading 'FirstGameUpload'\n", downloadCount+1);
  307. // Download again (First game)
  308. DownloadTable();
  309. if (HaltOnUnexpectedResult(rr=ReadResult(httpResult), RR_READ_TABLE))
  310. {PrintHttpResult(httpResult); return false;}
  311. // Check results
  312. // DOn't have this stored anymore
  313. // if (VerifyDownloadMatchesUpload(2,0)==false)
  314. // {PrintHttpResult(httpResult); return false;}
  315. printf("*** (%i) Downloading 'SecondGameUpload'\n", downloadCount+1);
  316. // Download again (second game)
  317. DownloadTable();
  318. if (HaltOnUnexpectedResult(rr=ReadResult(httpResult), RR_READ_TABLE))
  319. {PrintHttpResult(httpResult); return false;}
  320. // Check results
  321. if (VerifyDownloadMatchesUpload(2,1)==false)
  322. {PrintHttpResult(httpResult); return false;}
  323. downloadCount++;
  324. RakSleep(1000);
  325. }
  326. printf("*** Waiting for 70 seconds to have elapsed...\n");
  327. RakSleep(70000 - (RakNet::GetTimeMS()-startTime));
  328. printf("*** Testing that table is now clear.\n");
  329. // Table should be cleared
  330. DownloadTable();
  331. if (HaltOnUnexpectedResult(rr=ReadResult(httpResult), RR_EMPTY_TABLE))
  332. {PrintHttpResult(httpResult); return false;}
  333. if (PassTestOnEmptyDownloadedTable()==false)
  334. {PrintHttpResult(httpResult); return false;}
  335. printf("*** Testing upload and download. No games should be downloaded.\n");
  336. phpDirectoryServer2->ClearFields();
  337. phpDirectoryServer2->SetField("TestField1","NULL");
  338. UploadAndDownloadTable("FirstGameUpload", 80);
  339. if (HaltOnUnexpectedResult(rr=ReadResult(httpResult), RR_EMPTY_TABLE))
  340. {PrintHttpResult(httpResult); return false;}
  341. if (PassTestOnEmptyDownloadedTable()==false)
  342. {PrintHttpResult(httpResult); return false;}
  343. printf("*** Testing upload and download. One game should be downloaded.\n");
  344. UploadAndDownloadTable("ThirdGameUpload", 80);
  345. if (HaltOnUnexpectedResult(rr=ReadResult(httpResult), RR_READ_TABLE))
  346. {PrintHttpResult(httpResult); return false;}
  347. if (VerifyDownloadMatchesUpload(1,0)==false)
  348. {PrintHttpResult(httpResult); return false;}
  349. return true;
  350. }
  351. void OutputBody(HTTPConnection& http, const char *path, const char *data, TCPInterface& tcp);
  352. void TestPHPDirectoryServer(int argc, char **argv)
  353. {
  354. printf("PHP Directory server 2.\n");
  355. printf("Similar to lightweight database, but uses common shared webservers.\n");
  356. printf("Set columns and one row for your game, and upload it to a\nviewable and downloadable webpage.\n");
  357. printf("Difficulty: Intermediate\n\n");
  358. // tcp = RakNet::OP_NEW<TCPInterface>(_FILE_AND_LINE_);
  359. // httpConnection = RakNet::OP_NEW<HTTPConnection>(_FILE_AND_LINE_);
  360. // phpDirectoryServer2 = RakNet::OP_NEW<PHPDirectoryServer2>(_FILE_AND_LINE_);
  361. // RakNet::TimeMS lastTouched = 0;
  362. char website[256];
  363. char pathToPHP[256];
  364. if (argc==3)
  365. {
  366. strcpy(website, argv[1]);
  367. strcpy(pathToPHP, argv[2]);
  368. }
  369. else
  370. {
  371. printf("Enter website, e.g. jenkinssoftware.com:\n");
  372. Gets(website,sizeof(website));
  373. if (website[0]==0)
  374. strcpy(website, "jenkinssoftware.com");
  375. printf("Enter path to DirectoryServer.php, e.g. raknet/DirectoryServer.php:\n");
  376. Gets(pathToPHP,sizeof(pathToPHP));
  377. if (pathToPHP[0]==0)
  378. strcpy(pathToPHP, "/raknet/DirectoryServer.php");
  379. }
  380. if (website[strlen(website)-1]!='/' && pathToPHP[0]!='/')
  381. {
  382. memmove(pathToPHP+1, pathToPHP, strlen(pathToPHP)+1);
  383. pathToPHP[0]='/';
  384. }
  385. // This creates an HTTP connection using TCPInterface. It allows you to Post messages to and parse messages from webservers.
  386. // The connection attempt is asynchronous, and is handled automatically as HTTPConnection::Update() is called
  387. httpConnection->Init(tcp, website);
  388. // This adds specific parsing functionality to HTTPConnection, in order to communicate with DirectoryServer.php
  389. phpDirectoryServer2->Init(httpConnection, pathToPHP);
  390. if (RunTest())
  391. {
  392. printf("All tests passed.\n");
  393. }
  394. char str[256];
  395. do
  396. {
  397. printf("\nPress q to quit.\n");
  398. Gets(str, sizeof(str));
  399. } while (str[0]!='q');
  400. // The destructor of each of these references the other, so delete in this order
  401. RakNet::OP_DELETE(phpDirectoryServer2,_FILE_AND_LINE_);
  402. RakNet::OP_DELETE(httpConnection,_FILE_AND_LINE_);
  403. RakNet::OP_DELETE(tcp,_FILE_AND_LINE_);
  404. }
  405. void TestGet(void)
  406. {
  407. printf("This is NOT a reliable way to download from a website. Use libcurl instead.\n");
  408. httpConnection->Init(tcp, "jenkinssoftware.com");
  409. httpConnection->Get("/trivia/ranking.php?t=single&places=6&top");
  410. while (1)
  411. {
  412. Packet *packet = tcp->Receive();
  413. if(packet)
  414. {
  415. //printf((char*) packet->data);
  416. httpConnection->ProcessTCPPacket(packet);
  417. tcp->DeallocatePacket(packet);
  418. }
  419. httpConnection->Update();
  420. if (httpConnection->IsBusy()==false)
  421. {
  422. RakString fileContents = httpConnection->Read();
  423. printf(fileContents.C_String());
  424. getche();
  425. return;
  426. }
  427. // Prevent 100% cpu usage
  428. RakSleep(30);
  429. }
  430. }
  431. int main(int argc, char **argv)
  432. {
  433. printf("PHP Directory server 2.\n");
  434. printf("Similar to lightweight database, but uses common shared webservers.\n");
  435. printf("Set columns and one row for your game, and upload it to a\nviewable and downloadable webpage.\n");
  436. printf("Difficulty: Intermediate\n\n");
  437. tcp = RakNet::OP_NEW<TCPInterface>(__FILE__,__LINE__);
  438. httpConnection = RakNet::OP_NEW<HTTPConnection>(__FILE__,__LINE__);
  439. phpDirectoryServer2 = RakNet::OP_NEW<PHPDirectoryServer2>(__FILE__,__LINE__);
  440. // RakNetTime lastTouched = 0;
  441. // Start the TCP thread. This is used for general TCP communication, whether it is for webpages, sending emails, or telnet
  442. tcp->Start(0, 64);
  443. TestPHPDirectoryServer(argc,argv);
  444. //TestGet();
  445. return 0;
  446. }
粤ICP备19079148号