123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595 |
- #include "NetManager.h"
- #include "DebugManager.h"
- #include "Compiler/BfSystem.h"
- #include "BeefySysLib/util/AllocDebug.h"
- USING_NS_BF;
- #ifdef BF_CURL
- #define CURL_STATICLIB
- #include "curl/curl.h"
- #include "curl/multi.h"
- static int TransferInfoCallback(void* userp,
- curl_off_t dltotal, curl_off_t dlnow,
- curl_off_t ultotal, curl_off_t ulnow)
- {
- NetRequest* netRequest = (NetRequest*)userp;
- if (netRequest->mCancelling)
- return 1;
- return 0;
- }
- static size_t WriteMemoryCallback(void* contents, size_t size, size_t nmemb, void* userp)
- {
- NetRequest* netRequest = (NetRequest*)userp;
- long response_code = 0;
- curl_easy_getinfo(netRequest->mCURL, CURLINFO_RESPONSE_CODE, &response_code);
- if (netRequest->mCancelling)
- {
- netRequest->mFailed = true;
- return 0;
- }
- if (response_code != 200)
- return 0;
- if (netRequest->mFailed)
- return 0;
- if (!netRequest->mOutFile.IsOpen())
- {
- RecursiveCreateDirectory(GetFileDir(netRequest->mOutPath));
- if (!netRequest->mOutFile.Open(netRequest->mOutTempPath, "wb"))
- {
- netRequest->Fail(StrFormat("Failed to create file '%s'", netRequest->mOutTempPath.c_str()));
- return 0;
- }
- }
- uint32 tickNow = BFTickCount();
- if (tickNow - netRequest->mLastUpdateTick >= 500)
- {
- curl_off_t downloadSize = 0;
- curl_easy_getinfo(netRequest->mCURL, CURLINFO_SIZE_DOWNLOAD_T, &downloadSize);
- curl_off_t length = 0;
- curl_easy_getinfo(netRequest->mCURL, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &length);
- if (netRequest->mShowTracking)
- {
- String msg = StrFormat("symsrv Getting '%s' - %dk", netRequest->mURL.c_str(), (int)(downloadSize / 1024));
- if (length > 0)
- {
- msg += StrFormat(" (%d%%)", (int)(downloadSize * 100 / length));
- }
- netRequest->mNetManager->mDebugManager->OutputRawMessage(msg);
- }
- netRequest->mLastUpdateTick = tickNow;
- }
- size_t realsize = size * nmemb;
- netRequest->mOutFile.Write(contents, (int)realsize);
- return realsize;
- }
- void NetRequest::Cleanup()
- {
- if (mCURLMulti != NULL)
- {
- curl_multi_remove_handle(mCURLMulti, mCURL);
- }
- if (mCURL != NULL)
- curl_easy_cleanup(mCURL);
- if (mCURLMulti != NULL)
- {
- curl_multi_cleanup(mCURLMulti);
- }
- }
- void NetRequest::Perform()
- {
- if (mCancelling)
- return;
- // {
- // mFailed = true;
- // return;
- // }
- BfLogDbg("NetManager starting get on %s\n", mURL.c_str());
- mNetManager->mDebugManager->OutputRawMessage(StrFormat("msgLo Getting '%s'\n", mURL.c_str()));
- mOutTempPath = mOutPath + "__partial";
-
- mCURLMulti = curl_multi_init();
- mCURL = curl_easy_init();
- if (mShowTracking)
- {
- mNetManager->mDebugManager->OutputRawMessage(StrFormat("symsrv Getting '%s'", mURL.c_str()));
- }
- //OutputDebugStrF("Getting '%s'\n", mURL.c_str());
- curl_easy_setopt(mCURL, CURLOPT_URL, mURL.c_str());
- curl_easy_setopt(mCURL, CURLOPT_WRITEDATA, (void*)this);
- curl_easy_setopt(mCURL, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
- curl_easy_setopt(mCURL, CURLOPT_XFERINFODATA, (void*)this);
- curl_easy_setopt(mCURL, CURLOPT_XFERINFOFUNCTION, TransferInfoCallback);
- curl_easy_setopt(mCURL, CURLOPT_FOLLOWLOCATION, 1L);
- curl_easy_setopt(mCURL, CURLOPT_NOPROGRESS, 0L);
- //auto result = curl_easy_perform(mCURL);
- CURLMcode mcode = curl_multi_add_handle(mCURLMulti, mCURL);
- if (mcode != CURLM_OK)
- {
- mFailed = true;
- return;
- }
-
- while (true)
- {
- int activeCount = 0;
- curl_multi_perform(mCURLMulti, &activeCount);
- if (activeCount == 0)
- break;
- int waitRet = 0;
- curl_multi_wait(mCURLMulti, NULL, 0, 20, &waitRet);
- if (mCancelling)
- {
- mFailed = true;
- return;
- }
- }
- // if (result != CURLE_OK)
- // {
- // mFailed = true;
- // return;
- // }
- long response_code = 0;
- curl_easy_getinfo(mCURL, CURLINFO_RESPONSE_CODE, &response_code);
- mNetManager->mDebugManager->OutputRawMessage(StrFormat("msgLo Result for '%s': %d\n", mURL.c_str(), response_code));
- if (response_code != 200)
- {
- mOutFile.Close();
- // Bad result
- mFailed = true;
- return;
- }
- BfLogDbg("NetManager successfully completed %s\n", mURL.c_str());
- if (mCancelOnSuccess != NULL)
- mNetManager->Cancel(mCancelOnSuccess);
- if (!mOutFile.IsOpen())
- {
- mFailed = true;
- return; // No data
- }
- mOutFile.Close();
- BfpFile_Delete(mOutPath.c_str(), NULL);
- BfpFileResult renameResult;
- BfpFile_Rename(mOutTempPath.c_str(), mOutPath.c_str(), &renameResult);
- if (renameResult != BfpFileResult_Ok)
- {
- mFailed = true;
- }
- }
- #else
- #include <windows.h>
- #include <wininet.h>
- #include <stdio.h>
- #pragma comment (lib, "wininet.lib")
- void NetRequest::Perform()
- {
- if (mCancelling)
- return;
- // {
- // mFailed = true;
- // return;
- // }
- BfLogDbg("NetManager starting get on %s\n", mURL.c_str());
- String protoName;
- String serverName;
- String objectName;
- int colonPos = (int)mURL.IndexOf("://");
- if (colonPos == -1)
- {
- Fail("Invalid URL");
- return;
- }
-
- protoName = mURL.Substring(0, colonPos);
- serverName = mURL.Substring(colonPos + 3);
-
- int slashPos = (int)serverName.IndexOf('/');
- if (slashPos != -1)
- {
- objectName = serverName.Substring(slashPos);
- serverName.RemoveToEnd(slashPos);
- }
- mOutTempPath = mOutPath + "__partial";
- HINTERNET hSession = NULL;
- HINTERNET hConnect = NULL;
- HINTERNET hHttpFile = NULL;
- defer
- {
- if (hHttpFile != NULL)
- InternetCloseHandle(hHttpFile);
- if (hConnect != NULL)
- InternetCloseHandle(hConnect);
- if (hSession != NULL)
- InternetCloseHandle(hSession);
- if (mOutFile.IsOpen())
- mOutFile.Close();
- };
-
- bool isHttp = protoName.Equals("http", StringImpl::CompareKind_OrdinalIgnoreCase);
- bool isHttps = protoName.Equals("https", StringImpl::CompareKind_OrdinalIgnoreCase);
- if ((!isHttp) && (!isHttps))
- {
- Fail("Invalid URL protocol");
- return;
- }
- hSession = InternetOpenW(
- L"Mozilla/5.0",
- INTERNET_OPEN_TYPE_PRECONFIG,
- NULL,
- NULL,
- 0);
- hConnect = InternetConnectW(
- hSession,
- UTF8Decode(serverName).c_str(),
- isHttps ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT,
- NULL,
- NULL,
- INTERNET_SERVICE_HTTP,
- 0,
- 0);
- DWORD httpOpenFlags = INTERNET_FLAG_RELOAD;
- if (isHttps)
- httpOpenFlags |= INTERNET_FLAG_SECURE;
- hHttpFile = HttpOpenRequestW(
- hConnect,
- L"GET",
- UTF8Decode(objectName).c_str(),
- NULL,
- NULL,
- NULL,
- httpOpenFlags,
- 0);
- if (!HttpSendRequestW(hHttpFile, NULL, 0, 0, 0))
- {
- int err = GetLastError();
- OutputDebugStrF("Failed: %s %X\n", mURL.c_str(), err);;
- Fail("Failed to send request");
- return;
- }
- DWORD statusCode = 0;
- DWORD length = sizeof(DWORD);
- HttpQueryInfo(hHttpFile, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &statusCode, &length, NULL);
- if (statusCode != 200)
- {
- Fail("Invalid HTTP response");
- return;
- }
- if (mShowTracking)
- {
- mNetManager->mDebugManager->OutputRawMessage(StrFormat("symsrv Getting '%s'", mURL.c_str()));
- }
- uint8 buffer[4096];
- while (true)
- {
- DWORD dwBytesRead = 0;
- BOOL bRead = InternetReadFile(hHttpFile, buffer, 4096, &dwBytesRead);
- if (dwBytesRead == 0)
- break;
- if (!bRead)
- {
- //printf("InternetReadFile error : <%lu>\n", GetLastError());
- Fail("Failed to receive");
- return;
- }
- if (!mOutFile.IsOpen())
- {
- RecursiveCreateDirectory(GetFileDir(mOutPath));
- if (!mOutFile.Open(mOutTempPath, "wb"))
- {
- Fail(StrFormat("Failed to create file '%s'", mOutTempPath.c_str()));
- return;
- }
- }
- mOutFile.Write(buffer, (int)dwBytesRead);
- }
- BfLogDbg("NetManager successfully completed %s\n", mURL.c_str());
- if (mCancelOnSuccess != NULL)
- mNetManager->Cancel(mCancelOnSuccess);
- if (!mOutFile.IsOpen())
- {
- mFailed = true;
- return; // No data
- }
- mOutFile.Close();
- BfpFile_Delete(mOutPath.c_str(), NULL);
- BfpFileResult renameResult;
- BfpFile_Rename(mOutTempPath.c_str(), mOutPath.c_str(), &renameResult);
- if (renameResult != BfpFileResult_Ok)
- mFailed = true;
- }
- void NetRequest::Cleanup()
- {
- }
- #endif
- NetRequest::~NetRequest()
- {
- Cleanup();
- // This is synchronized
- if (mResult != NULL)
- {
- mResult->mFailed = mFailed;
- mResult->mCurRequest = NULL;
- if (mResult->mRemoved)
- delete mResult;
- }
- mNetManager->mRequestDoneEvent.Set();
- }
- void NetRequest::Fail(const StringImpl& error)
- {
- if (mFailed)
- return;
- mError = error;
- mFailed = true;
- }
- bool NetRequest::Cancel()
- {
- mCancelling = true;
- return true;
- }
- void NetRequest::ShowTracking()
- {
- //mNetManager->mDebugManager->OutputMessage(StrFormat("Getting '%s'\n", mURL.c_str()));
- mNetManager->mDebugManager->OutputRawMessage(StrFormat("symsrv Getting '%s'", mURL.c_str()));
- mShowTracking = true;
- }
- void NetManagerThread()
- {
- }
- NetManager::NetManager() : mThreadPool(8, 1*1024*1024)
- {
- mWaitingResult = NULL;
- mWaitingRequest = NULL;
- }
- NetManager::~NetManager()
- {
- mThreadPool.Shutdown();
- for (auto kv : mCachedResults)
- {
- delete kv.mValue;
- }
- }
- NetRequest* NetManager::CreateGetRequest(const StringImpl& url, const StringImpl& destPath)
- {
- AutoCrit autoCrit(mThreadPool.mCritSect);
- NetRequest* netRequest = new NetRequest();
- netRequest->mNetManager = this;
- netRequest->mURL = url;
- netRequest->mOutPath = destPath;
- NetResult* netResult = new NetResult();
- netResult->mURL = url;
- netResult->mOutPath = destPath;
- netResult->mFailed = false;
- netResult->mCurRequest = netRequest;
- NetResult** netResultPtr;
- if (mCachedResults.TryAdd(url, NULL, &netResultPtr))
- {
- *netResultPtr = netResult;
- }
- else
- {
- mOldResults.Add(*netResultPtr);
- *netResultPtr = netResult;
- }
- netRequest->mResult = netResult;
- return netRequest;
- }
- NetResult* NetManager::QueueGet(const StringImpl& url, const StringImpl& destPath)
- {
- BfLogDbg("NetManager queueing %s\n", url.c_str());
- auto netRequest = CreateGetRequest(url, destPath);
- auto netResult = netRequest->mResult;
- mThreadPool.AddJob(netRequest);
- return netResult;
- }
- bool NetManager::Get(const StringImpl& url, const StringImpl& destPath)
- {
- NetRequest* netRequest = NULL;
- int waitCount = 0;
- while (true)
- {
- // Check cached
- {
- AutoCrit autoCrit(mThreadPool.mCritSect);
- mWaitingResult = NULL;
- NetResult* netResult;
- if (mCachedResults.TryGetValue(url, &netResult))
- {
- if (netResult->mCurRequest == NULL)
- {
- BfLogDbg("NetManager::Get using cached result for %s: %d. WaitCount: %d \n", url.c_str(), !netResult->mFailed, waitCount);
- return (!netResult->mFailed) && (FileExists(netResult->mOutPath));
- }
- else if (!netResult->mCurRequest->mShowTracking) // Is done?
- {
- if (!netResult->mCurRequest->mProcessing)
- {
- BfLogDbg("NetManager::Get pulling queued request into current thread %s\n", url.c_str());
- netRequest = netResult->mCurRequest;
- bool didRemove = mThreadPool.mJobs.Remove(netRequest);
- BF_ASSERT(didRemove);
- break;
- }
- mWaitingResult = netResult;
- netResult->mCurRequest->ShowTracking();
- }
- }
- else
- {
- netRequest = CreateGetRequest(url, destPath);
- break;
- }
- }
- waitCount++;
- mRequestDoneEvent.WaitFor();
- }
- // Perform this in the requesting thread
- {
- AutoCrit autoCrit(mThreadPool.mCritSect);
- mWaitingRequest = netRequest;
- }
- netRequest->mShowTracking = true;
- netRequest->Perform();
- AutoCrit autoCrit(mThreadPool.mCritSect);
- mWaitingRequest = NULL;
- auto netResult = netRequest->mResult;
- delete netRequest;
- BfLogDbg("NetManager::Get requested %s: %d\n", url.c_str(), !netResult->mFailed);
- mDebugManager->OutputRawMessage(StrFormat("msgLo Result for '%s': %d\n", url.c_str(), !netResult->mFailed));
- return (!netResult->mFailed) && (FileExists(netResult->mOutPath));
- }
- void NetManager::CancelAll()
- {
- AutoCrit autoCrit(mThreadPool.mCritSect);
- if (mWaitingRequest != NULL)
- mWaitingRequest->Cancel();
- mThreadPool.CancelAll();
- }
- void NetManager::Clear()
- {
- AutoCrit autoCrit(mThreadPool.mCritSect);
- BF_ASSERT(mWaitingResult == NULL); // The debugger thread shouldn't be waiting on anything, it should be detached at this point
- for (auto job : mThreadPool.mJobs)
- {
- auto netRequest = (NetRequest*)job;
- netRequest->Cancel();
- }
- for (auto kv : mCachedResults)
- {
- NetResult* netResult = kv.mValue;
- if (netResult->mCurRequest != NULL)
- netResult->mRemoved = true;
- else
- delete netResult;
- }
- mCachedResults.Clear();
- for (auto netResult : mOldResults)
- {
- delete netResult;
- }
- mOldResults.Clear();
- }
- void NetManager::CancelCurrent()
- {
- AutoCrit autoCrit(mThreadPool.mCritSect);
- if (mWaitingRequest != NULL)
- mWaitingRequest->Cancel();
- else if ((mWaitingResult != NULL) && (mWaitingResult->mCurRequest != NULL))
- mWaitingResult->mCurRequest->Cancel();
- }
- void NetManager::Cancel(NetResult* netResult)
- {
- AutoCrit autoCrit(mThreadPool.mCritSect);
- BfLogDbg("NetManager cancel %s\n", netResult->mURL.c_str());
- if (netResult->mCurRequest != NULL)
- netResult->mCurRequest->Cancel();
- }
- void NetManager::SetCancelOnSuccess(NetResult* dependentResult, NetResult* cancelOnSucess)
- {
- AutoCrit autoCrit(mThreadPool.mCritSect);
- if (dependentResult->mCurRequest != NULL)
- {
- dependentResult->mCurRequest->mCancelOnSuccess = cancelOnSucess;
- }
- }
|