NetManager.cpp 14 KB

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