NetManager.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. #include "NetManager.h"
  2. #include "DebugManager.h"
  3. #include "Compiler/BfSystem.h"
  4. #include "BeefySysLib/util/AllocDebug.h"
  5. USING_NS_BF;
  6. #ifdef BF_CURL
  7. #define CURL_STATICLIB
  8. #include "curl/curl.h"
  9. #include "curl/multi.h"
  10. static int TransferInfoCallback(void* userp,
  11. curl_off_t dltotal, curl_off_t dlnow,
  12. curl_off_t ultotal, curl_off_t ulnow)
  13. {
  14. NetRequest* netRequest = (NetRequest*)userp;
  15. if (netRequest->mCancelling)
  16. return 1;
  17. return 0;
  18. }
  19. static size_t WriteMemoryCallback(void* contents, size_t size, size_t nmemb, void* userp)
  20. {
  21. NetRequest* netRequest = (NetRequest*)userp;
  22. long response_code = 0;
  23. curl_easy_getinfo(netRequest->mCURL, CURLINFO_RESPONSE_CODE, &response_code);
  24. if (netRequest->mCancelling)
  25. {
  26. netRequest->mFailed = true;
  27. return 0;
  28. }
  29. if (response_code != 200)
  30. return 0;
  31. if (netRequest->mFailed)
  32. return 0;
  33. if (!netRequest->mOutFile.IsOpen())
  34. {
  35. RecursiveCreateDirectory(GetFileDir(netRequest->mOutPath));
  36. if (!netRequest->mOutFile.Open(netRequest->mOutTempPath, "wb"))
  37. {
  38. netRequest->Fail(StrFormat("Failed to create file '%s'", netRequest->mOutTempPath.c_str()));
  39. return 0;
  40. }
  41. }
  42. uint32 tickNow = BFTickCount();
  43. if (tickNow - netRequest->mLastUpdateTick >= 500)
  44. {
  45. curl_off_t downloadSize = 0;
  46. curl_easy_getinfo(netRequest->mCURL, CURLINFO_SIZE_DOWNLOAD_T, &downloadSize);
  47. curl_off_t length = 0;
  48. curl_easy_getinfo(netRequest->mCURL, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &length);
  49. if (netRequest->mShowTracking)
  50. {
  51. String msg = StrFormat("symsrv Getting '%s' - %dk", netRequest->mURL.c_str(), (int)(downloadSize / 1024));
  52. if (length > 0)
  53. {
  54. msg += StrFormat(" (%d%%)", (int)(downloadSize * 100 / length));
  55. }
  56. netRequest->mNetManager->mDebugManager->OutputRawMessage(msg);
  57. }
  58. netRequest->mLastUpdateTick = tickNow;
  59. }
  60. size_t realsize = size * nmemb;
  61. netRequest->mOutFile.Write(contents, (int)realsize);
  62. return realsize;
  63. }
  64. void NetRequest::Cleanup()
  65. {
  66. if (mCURLMulti != NULL)
  67. {
  68. curl_multi_remove_handle(mCURLMulti, mCURL);
  69. }
  70. if (mCURL != NULL)
  71. curl_easy_cleanup(mCURL);
  72. if (mCURLMulti != NULL)
  73. {
  74. curl_multi_cleanup(mCURLMulti);
  75. }
  76. }
  77. void NetRequest::DoTransfer()
  78. {
  79. if (mCancelling)
  80. return;
  81. // {
  82. // mFailed = true;
  83. // return;
  84. // }
  85. BfLogDbg("NetManager starting get on %s\n", mURL.c_str());
  86. mNetManager->mDebugManager->OutputRawMessage(StrFormat("msgLo Getting '%s'\n", mURL.c_str()));
  87. mOutTempPath = mOutPath + "__partial";
  88. mCURLMulti = curl_multi_init();
  89. mCURL = curl_easy_init();
  90. if (mShowTracking)
  91. {
  92. mNetManager->mDebugManager->OutputRawMessage(StrFormat("symsrv Getting '%s'", mURL.c_str()));
  93. }
  94. //OutputDebugStrF("Getting '%s'\n", mURL.c_str());
  95. curl_easy_setopt(mCURL, CURLOPT_URL, mURL.c_str());
  96. curl_easy_setopt(mCURL, CURLOPT_WRITEDATA, (void*)this);
  97. curl_easy_setopt(mCURL, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
  98. curl_easy_setopt(mCURL, CURLOPT_XFERINFODATA, (void*)this);
  99. curl_easy_setopt(mCURL, CURLOPT_XFERINFOFUNCTION, TransferInfoCallback);
  100. curl_easy_setopt(mCURL, CURLOPT_FOLLOWLOCATION, 1L);
  101. curl_easy_setopt(mCURL, CURLOPT_NOPROGRESS, 0L);
  102. //auto result = curl_easy_perform(mCURL);
  103. CURLMcode mcode = curl_multi_add_handle(mCURLMulti, mCURL);
  104. if (mcode != CURLM_OK)
  105. {
  106. mFailed = true;
  107. return;
  108. }
  109. while (true)
  110. {
  111. int activeCount = 0;
  112. curl_multi_perform(mCURLMulti, &activeCount);
  113. if (activeCount == 0)
  114. break;
  115. int waitRet = 0;
  116. curl_multi_wait(mCURLMulti, NULL, 0, 20, &waitRet);
  117. if (mCancelling)
  118. {
  119. mFailed = true;
  120. return;
  121. }
  122. }
  123. // if (result != CURLE_OK)
  124. // {
  125. // mFailed = true;
  126. // return;
  127. // }
  128. long response_code = 0;
  129. curl_easy_getinfo(mCURL, CURLINFO_RESPONSE_CODE, &response_code);
  130. mNetManager->mDebugManager->OutputRawMessage(StrFormat("msgLo Result for '%s': %d\n", mURL.c_str(), response_code));
  131. if (response_code != 200)
  132. {
  133. mOutFile.Close();
  134. // Bad result
  135. mFailed = true;
  136. return;
  137. }
  138. BfLogDbg("NetManager successfully completed %s\n", mURL.c_str());
  139. if (mCancelOnSuccess != NULL)
  140. mNetManager->Cancel(mCancelOnSuccess);
  141. if (!mOutFile.IsOpen())
  142. {
  143. mFailed = true;
  144. return; // No data
  145. }
  146. mOutFile.Close();
  147. BfpFile_Delete(mOutPath.c_str(), NULL);
  148. BfpFileResult renameResult;
  149. BfpFile_Rename(mOutTempPath.c_str(), mOutPath.c_str(), &renameResult);
  150. if (renameResult != BfpFileResult_Ok)
  151. {
  152. mFailed = true;
  153. }
  154. }
  155. void NetRequest::Perform()
  156. {
  157. DoTransfer();
  158. }
  159. #elif defined BF_PLATFORM_WINDOWS
  160. #include <windows.h>
  161. #include <wininet.h>
  162. #include <stdio.h>
  163. #pragma comment (lib, "wininet.lib")
  164. void NetRequest::Perform()
  165. {
  166. if (mCancelling)
  167. return;
  168. // {
  169. // mFailed = true;
  170. // return;
  171. // }
  172. BfLogDbg("NetManager starting get on %s\n", mURL.c_str());
  173. String protoName;
  174. String serverName;
  175. String objectName;
  176. int colonPos = (int)mURL.IndexOf("://");
  177. if (colonPos == -1)
  178. {
  179. Fail("Invalid URL");
  180. return;
  181. }
  182. protoName = mURL.Substring(0, colonPos);
  183. serverName = mURL.Substring(colonPos + 3);
  184. int slashPos = (int)serverName.IndexOf('/');
  185. if (slashPos != -1)
  186. {
  187. objectName = serverName.Substring(slashPos);
  188. serverName.RemoveToEnd(slashPos);
  189. }
  190. mOutTempPath = mOutPath + "__partial";
  191. HINTERNET hSession = NULL;
  192. HINTERNET hConnect = NULL;
  193. HINTERNET hHttpFile = NULL;
  194. defer
  195. {
  196. if (hHttpFile != NULL)
  197. InternetCloseHandle(hHttpFile);
  198. if (hConnect != NULL)
  199. InternetCloseHandle(hConnect);
  200. if (hSession != NULL)
  201. InternetCloseHandle(hSession);
  202. if (mOutFile.IsOpen())
  203. mOutFile.Close();
  204. };
  205. bool isHttp = protoName.Equals("http", StringImpl::CompareKind_OrdinalIgnoreCase);
  206. bool isHttps = protoName.Equals("https", StringImpl::CompareKind_OrdinalIgnoreCase);
  207. if ((!isHttp) && (!isHttps))
  208. {
  209. Fail("Invalid URL protocol");
  210. return;
  211. }
  212. hSession = InternetOpenW(
  213. L"Mozilla/5.0",
  214. INTERNET_OPEN_TYPE_PRECONFIG,
  215. NULL,
  216. NULL,
  217. 0);
  218. hConnect = InternetConnectW(
  219. hSession,
  220. UTF8Decode(serverName).c_str(),
  221. isHttps ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT,
  222. NULL,
  223. NULL,
  224. INTERNET_SERVICE_HTTP,
  225. 0,
  226. 0);
  227. DWORD httpOpenFlags = INTERNET_FLAG_RELOAD;
  228. if (isHttps)
  229. httpOpenFlags |= INTERNET_FLAG_SECURE;
  230. hHttpFile = HttpOpenRequestW(
  231. hConnect,
  232. L"GET",
  233. UTF8Decode(objectName).c_str(),
  234. NULL,
  235. NULL,
  236. NULL,
  237. httpOpenFlags,
  238. 0);
  239. if (!HttpSendRequestW(hHttpFile, NULL, 0, 0, 0))
  240. {
  241. int err = GetLastError();
  242. OutputDebugStrF("Failed: %s %X\n", mURL.c_str(), err);;
  243. Fail("Failed to send request");
  244. return;
  245. }
  246. DWORD statusCode = 0;
  247. DWORD length = sizeof(DWORD);
  248. HttpQueryInfo(hHttpFile, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &statusCode, &length, NULL);
  249. if (statusCode != 200)
  250. {
  251. Fail("Invalid HTTP response");
  252. return;
  253. }
  254. if (mShowTracking)
  255. {
  256. mNetManager->mDebugManager->OutputRawMessage(StrFormat("symsrv Getting '%s'", mURL.c_str()));
  257. }
  258. uint8 buffer[4096];
  259. while (true)
  260. {
  261. DWORD dwBytesRead = 0;
  262. BOOL bRead = InternetReadFile(hHttpFile, buffer, 4096, &dwBytesRead);
  263. if (dwBytesRead == 0)
  264. break;
  265. if (!bRead)
  266. {
  267. //printf("InternetReadFile error : <%lu>\n", GetLastError());
  268. Fail("Failed to receive");
  269. return;
  270. }
  271. if (!mOutFile.IsOpen())
  272. {
  273. RecursiveCreateDirectory(GetFileDir(mOutPath));
  274. if (!mOutFile.Open(mOutTempPath, "wb"))
  275. {
  276. Fail(StrFormat("Failed to create file '%s'", mOutTempPath.c_str()));
  277. return;
  278. }
  279. }
  280. mOutFile.Write(buffer, (int)dwBytesRead);
  281. }
  282. BfLogDbg("NetManager successfully completed %s\n", mURL.c_str());
  283. if (mCancelOnSuccess != NULL)
  284. mNetManager->Cancel(mCancelOnSuccess);
  285. if (!mOutFile.IsOpen())
  286. {
  287. mFailed = true;
  288. return; // No data
  289. }
  290. mOutFile.Close();
  291. BfpFile_Delete(mOutPath.c_str(), NULL);
  292. BfpFileResult renameResult;
  293. BfpFile_Rename(mOutTempPath.c_str(), mOutPath.c_str(), &renameResult);
  294. if (renameResult != BfpFileResult_Ok)
  295. mFailed = true;
  296. }
  297. void NetRequest::Cleanup()
  298. {
  299. }
  300. #else
  301. void NetRequest::Perform()
  302. {
  303. mFailed = true;
  304. }
  305. void NetRequest::Cleanup()
  306. {
  307. }
  308. #endif
  309. NetRequest::~NetRequest()
  310. {
  311. Cleanup();
  312. // This is synchronized
  313. if (mResult != NULL)
  314. {
  315. mResult->mFailed = mFailed;
  316. mResult->mCurRequest = NULL;
  317. if (mResult->mDoneEvent != NULL)
  318. {
  319. mResult->mDoneEvent->Set(true);
  320. BF_ASSERT(!mResult->mRemoved);
  321. }
  322. if (mResult->mRemoved)
  323. delete mResult;
  324. }
  325. mNetManager->mRequestDoneEvent.Set();
  326. }
  327. void NetRequest::Fail(const StringImpl& error)
  328. {
  329. if (mFailed)
  330. return;
  331. mError = error;
  332. mFailed = true;
  333. }
  334. bool NetRequest::Cancel()
  335. {
  336. mCancelling = true;
  337. return true;
  338. }
  339. void NetRequest::ShowTracking()
  340. {
  341. //mNetManager->mDebugManager->OutputMessage(StrFormat("Getting '%s'\n", mURL.c_str()));
  342. mNetManager->mDebugManager->OutputRawMessage(StrFormat("symsrv Getting '%s'", mURL.c_str()));
  343. mShowTracking = true;
  344. }
  345. void NetManagerThread()
  346. {
  347. }
  348. NetManager::NetManager() : mThreadPool(8, 1*1024*1024)
  349. {
  350. mWaitingResult = NULL;
  351. mWaitingRequest = NULL;
  352. }
  353. NetManager::~NetManager()
  354. {
  355. mThreadPool.Shutdown();
  356. for (auto kv : mCachedResults)
  357. {
  358. delete kv.mValue;
  359. }
  360. }
  361. NetRequest* NetManager::CreateGetRequest(const StringImpl& url, const StringImpl& destPath, bool useCache)
  362. {
  363. AutoCrit autoCrit(mThreadPool.mCritSect);
  364. NetRequest* netRequest = new NetRequest();
  365. netRequest->mNetManager = this;
  366. netRequest->mURL = url;
  367. netRequest->mOutPath = destPath;
  368. NetResult* netResult = new NetResult();
  369. netResult->mURL = url;
  370. netResult->mOutPath = destPath;
  371. netResult->mFailed = false;
  372. netResult->mCurRequest = netRequest;
  373. if (useCache)
  374. {
  375. NetResult** netResultPtr;
  376. if (mCachedResults.TryAdd(url, NULL, &netResultPtr))
  377. {
  378. *netResultPtr = netResult;
  379. }
  380. else
  381. {
  382. mOldResults.Add(*netResultPtr);
  383. *netResultPtr = netResult;
  384. }
  385. }
  386. netRequest->mResult = netResult;
  387. return netRequest;
  388. }
  389. NetResult* NetManager::QueueGet(const StringImpl& url, const StringImpl& destPath, bool useCache)
  390. {
  391. BfLogDbg("NetManager queueing %s %d\n", url.c_str(), useCache);
  392. auto netRequest = CreateGetRequest(url, destPath, useCache);
  393. auto netResult = netRequest->mResult;
  394. mThreadPool.AddJob(netRequest);
  395. return netResult;
  396. }
  397. bool NetManager::Get(const StringImpl& url, const StringImpl& destPath)
  398. {
  399. NetRequest* netRequest = NULL;
  400. int waitCount = 0;
  401. while (true)
  402. {
  403. // Check cached
  404. {
  405. AutoCrit autoCrit(mThreadPool.mCritSect);
  406. mWaitingResult = NULL;
  407. NetResult* netResult;
  408. if (mCachedResults.TryGetValue(url, &netResult))
  409. {
  410. if (netResult->mCurRequest == NULL)
  411. {
  412. BfLogDbg("NetManager::Get using cached result for %s: %d. WaitCount: %d \n", url.c_str(), !netResult->mFailed, waitCount);
  413. return (!netResult->mFailed) && (FileExists(netResult->mOutPath));
  414. }
  415. else if (!netResult->mCurRequest->mShowTracking) // Is done?
  416. {
  417. if (!netResult->mCurRequest->mProcessing)
  418. {
  419. BfLogDbg("NetManager::Get pulling queued request into current thread %s\n", url.c_str());
  420. netRequest = netResult->mCurRequest;
  421. bool didRemove = mThreadPool.mJobs.Remove(netRequest);
  422. BF_ASSERT(didRemove);
  423. break;
  424. }
  425. mWaitingResult = netResult;
  426. netResult->mCurRequest->ShowTracking();
  427. }
  428. }
  429. else
  430. {
  431. netRequest = CreateGetRequest(url, destPath, true);
  432. break;
  433. }
  434. }
  435. waitCount++;
  436. mRequestDoneEvent.WaitFor();
  437. }
  438. // Perform this in the requesting thread
  439. {
  440. AutoCrit autoCrit(mThreadPool.mCritSect);
  441. mWaitingRequest = netRequest;
  442. }
  443. netRequest->mShowTracking = true;
  444. netRequest->Perform();
  445. AutoCrit autoCrit(mThreadPool.mCritSect);
  446. mWaitingRequest = NULL;
  447. auto netResult = netRequest->mResult;
  448. delete netRequest;
  449. BfLogDbg("NetManager::Get requested %s: %d\n", url.c_str(), !netResult->mFailed);
  450. mDebugManager->OutputRawMessage(StrFormat("msgLo Result for '%s': %d\n", url.c_str(), !netResult->mFailed));
  451. return (!netResult->mFailed) && (FileExists(netResult->mOutPath));
  452. }
  453. void NetManager::CancelAll()
  454. {
  455. AutoCrit autoCrit(mThreadPool.mCritSect);
  456. if (mWaitingRequest != NULL)
  457. mWaitingRequest->Cancel();
  458. mThreadPool.CancelAll();
  459. }
  460. void NetManager::Clear()
  461. {
  462. AutoCrit autoCrit(mThreadPool.mCritSect);
  463. BF_ASSERT(mWaitingResult == NULL); // The debugger thread shouldn't be waiting on anything, it should be detached at this point
  464. for (auto job : mThreadPool.mJobs)
  465. {
  466. auto netRequest = (NetRequest*)job;
  467. netRequest->Cancel();
  468. }
  469. for (auto kv : mCachedResults)
  470. {
  471. NetResult* netResult = kv.mValue;
  472. if (netResult->mCurRequest != NULL)
  473. netResult->mRemoved = true;
  474. else
  475. delete netResult;
  476. }
  477. mCachedResults.Clear();
  478. for (auto netResult : mOldResults)
  479. {
  480. delete netResult;
  481. }
  482. mOldResults.Clear();
  483. }
  484. void NetManager::CancelCurrent()
  485. {
  486. AutoCrit autoCrit(mThreadPool.mCritSect);
  487. if (mWaitingRequest != NULL)
  488. mWaitingRequest->Cancel();
  489. else if ((mWaitingResult != NULL) && (mWaitingResult->mCurRequest != NULL))
  490. mWaitingResult->mCurRequest->Cancel();
  491. }
  492. void NetManager::Cancel(NetResult* netResult)
  493. {
  494. AutoCrit autoCrit(mThreadPool.mCritSect);
  495. BfLogDbg("NetManager cancel %s\n", netResult->mURL.c_str());
  496. if (netResult->mCurRequest != NULL)
  497. netResult->mCurRequest->Cancel();
  498. }
  499. void NetManager::SetCancelOnSuccess(NetResult* dependentResult, NetResult* cancelOnSucess)
  500. {
  501. AutoCrit autoCrit(mThreadPool.mCritSect);
  502. if (dependentResult->mCurRequest != NULL)
  503. {
  504. dependentResult->mCurRequest->mCancelOnSuccess = cancelOnSucess;
  505. }
  506. }