[win] updatesvc: refactoring cdownloader

This commit is contained in:
SimplestStudio
2023-10-23 13:17:16 +03:00
parent d8d4739528
commit 364c3d5f53
2 changed files with 135 additions and 124 deletions

View File

@ -33,6 +33,7 @@
#include "cdownloader.h"
#include <Windows.h>
#include <Winhttp.h>
#include <future>
#define DNL_OK 0
#define DNL_ABORT 1
@ -43,7 +44,16 @@
#define DNL_OTHER_ERR 6
int downloadToFile(const wstring &url, const wstring &filePath, std::atomic_bool &run, FnVoidInt &progress_callback)
struct Connection {
~Connection() {
if (hRequest) WinHttpCloseHandle(hRequest);
if (hConnect) WinHttpCloseHandle(hConnect);
if (hSession) WinHttpCloseHandle(hSession);
}
HINTERNET hSession = NULL, hConnect = NULL, hRequest = NULL;
};
static int initConnection(const wstring &url, DWORD &dwFileSize, Connection &conn)
{
URL_COMPONENTS urlComp;
ZeroMemory(&urlComp, sizeof(urlComp));
@ -56,144 +66,149 @@ int downloadToFile(const wstring &url, const wstring &filePath, std::atomic_bool
wstring url_host(urlComp.lpszHostName, urlComp.dwHostNameLength);
wstring url_path(urlComp.lpszUrlPath, urlComp.dwUrlPathLength);
int prev_percent = -1;
DWORD dwProgress = 0;
DWORD dwProgressMax = 0;
DWORD dwSize = sizeof(DWORD);
HANDLE hFile = INVALID_HANDLE_VALUE;
HINTERNET hSession = WinHttpOpen(L"WinHTTP Example/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (!hSession)
conn.hSession = WinHttpOpen(L"WinHTTP Example/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (!conn.hSession)
return DNL_OTHER_ERR;
//WinHttpSetStatusCallback(hSession, WinHttpCallback, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0);
DWORD dwEnabledProtocols = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2;
WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &dwEnabledProtocols, sizeof(DWORD));
WinHttpSetOption(conn.hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &dwEnabledProtocols, sizeof(DWORD));
HINTERNET hConnect = WinHttpConnect(hSession, url_host.c_str(), INTERNET_DEFAULT_HTTPS_PORT, 0);
if (!hConnect) {
WinHttpCloseHandle(hSession);
conn.hConnect = WinHttpConnect(conn.hSession, url_host.c_str(), INTERNET_DEFAULT_HTTPS_PORT, 0);
if (!conn.hConnect)
return DNL_CONN_ERR;
}
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET", url_path.c_str(), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
if (!hRequest) {
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
conn.hRequest = WinHttpOpenRequest(conn.hConnect, L"GET", url_path.c_str(), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
if (!conn.hRequest)
return DNL_CONN_ERR;
}
int result = DNL_OK;
if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0)) {
result = DNL_OTHER_ERR;
goto cleanup;
}
if (!WinHttpSendRequest(conn.hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0))
return DNL_OTHER_ERR;
if (!WinHttpReceiveResponse(hRequest, NULL)) {
result = DNL_CONN_ERR;
goto cleanup;
}
if (!WinHttpReceiveResponse(conn.hRequest, NULL))
return DNL_CONN_ERR;
if (!WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER, NULL, &dwProgressMax, &dwSize, WINHTTP_NO_HEADER_INDEX)) {
result = DNL_CONN_ERR;
goto cleanup;
}
DWORD dwSize = sizeof(DWORD);
if (!WinHttpQueryHeaders(conn.hRequest, WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER, NULL, &dwFileSize, &dwSize, WINHTTP_NO_HEADER_INDEX))
return DNL_CONN_ERR;
hFile = CreateFile(filePath.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
result = DNL_CREAT_ERR;
goto cleanup;
}
do {
if (!run) {
result = DNL_ABORT;
break;
}
dwSize = 0;
if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
result = DNL_CONN_ERR;
break;
}
LPSTR lpBuffer = new char[dwSize];
if (!lpBuffer) {
result = DNL_OUT_MEM;
break;
}
DWORD dwDownloaded = 0;
if (!WinHttpReadData(hRequest, (LPVOID)lpBuffer, dwSize, &dwDownloaded)) {
result = DNL_CONN_ERR;
delete[] lpBuffer;
break;
}
DWORD dwBytesWritten = 0;
BOOL write_res = WriteFile(hFile, lpBuffer, dwDownloaded, &dwBytesWritten, NULL);
delete[] lpBuffer;
if (!write_res || dwBytesWritten != dwDownloaded) {
result = DNL_OUT_MEM;
break;
}
if (dwProgressMax != 0 && progress_callback) {
dwProgress += dwDownloaded;
int percent = static_cast<int>((100.0 * dwProgress) / dwProgressMax);
if (percent != prev_percent) {
progress_callback(percent);
prev_percent = percent;
}
}
} while (dwSize > 0);
cleanup :
if (hFile != INVALID_HANDLE_VALUE) {
CloseHandle(hFile);
if (result == DNL_ABORT)
DeleteFile(filePath.c_str());
}
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return result;
return DNL_OK;
}
CDownloader::CDownloader()
class CDownloaderPrivate
{
m_run = true;
m_lock = false;
public:
CDownloaderPrivate()
{}
~CDownloaderPrivate()
{}
int downloadToFile()
{
DWORD dwSize = 0, dwProgress = 0, dwProgressMax = 0;
Connection conn;
int result = initConnection(m_url, dwProgressMax, conn);
if (result != DNL_OK)
return result;
HANDLE hFile = CreateFile(m_filePath.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return DNL_CREAT_ERR;
int prev_percent = -1;
do {
if (!m_run) {
result = DNL_ABORT;
break;
}
dwSize = 0;
if (!WinHttpQueryDataAvailable(conn.hRequest, &dwSize)) {
result = DNL_CONN_ERR;
break;
}
LPSTR lpBuffer = new char[dwSize];
if (!lpBuffer) {
result = DNL_OUT_MEM;
break;
}
DWORD dwDownloaded = 0;
if (!WinHttpReadData(conn.hRequest, (LPVOID)lpBuffer, dwSize, &dwDownloaded)) {
result = DNL_CONN_ERR;
delete[] lpBuffer;
break;
}
DWORD dwBytesWritten = 0;
BOOL write_res = WriteFile(hFile, lpBuffer, dwDownloaded, &dwBytesWritten, NULL);
delete[] lpBuffer;
if (!write_res || dwBytesWritten != dwDownloaded) {
result = DNL_OUT_MEM;
break;
}
if (dwProgressMax != 0 && m_progress_callback) {
dwProgress += dwDownloaded;
int percent = static_cast<int>((100.0 * dwProgress) / dwProgressMax);
if (percent != prev_percent) {
m_progress_callback(percent);
prev_percent = percent;
}
}
} while (dwSize > 0);
CloseHandle(hFile);
if (result == DNL_ABORT)
DeleteFile(m_filePath.c_str());
return result;
}
FnVoidInt m_complete_callback = nullptr,
m_progress_callback = nullptr;
wstring m_url,
m_filePath;
std::future<void> m_future;
std::atomic_bool m_run,
m_lock;
};
CDownloader::CDownloader() :
pimpl(new CDownloaderPrivate)
{
pimpl->m_run = true;
pimpl->m_lock = false;
}
CDownloader::~CDownloader()
{
m_run = false;
if (m_future.valid())
m_future.wait();
pimpl->m_run = false;
if (pimpl->m_future.valid())
pimpl->m_future.wait();
delete pimpl, pimpl = nullptr;
}
void CDownloader::downloadFile(const std::wstring &url, const std::wstring &filePath)
{
m_url.clear();
m_filePath.clear();
if (url.empty() || filePath.empty() || m_lock)
pimpl->m_url.clear();
pimpl->m_filePath.clear();
if (url.empty() || filePath.empty() || pimpl->m_lock)
return;
m_url = url;
m_filePath = filePath;
pimpl->m_url = url;
pimpl->m_filePath = filePath;
start();
}
void CDownloader::start()
{
if (m_url.empty() || m_filePath.empty() || m_lock)
if (pimpl->m_url.empty() || pimpl->m_filePath.empty() || pimpl->m_lock)
return;
m_run = true;
m_lock = true;
m_future = std::async(std::launch::async, [=]() {
int hr = downloadToFile(m_url, m_filePath, m_run, m_progress_callback);
pimpl->m_run = true;
pimpl->m_lock = true;
pimpl->m_future = std::async(std::launch::async, [=]() {
int hr = pimpl->downloadToFile();
int error = (hr == DNL_OK) ? 0 :
(hr == DNL_ABORT) ? 1 :
(hr == DNL_OUT_MEM) ? -1 :
@ -201,28 +216,28 @@ void CDownloader::start()
(hr == DNL_URL_ERR) ? -3 :
(hr == DNL_CREAT_ERR) ? -4 : -5;
if (m_complete_callback)
m_complete_callback(error);
m_lock = false;
if (pimpl->m_complete_callback)
pimpl->m_complete_callback(error);
pimpl->m_lock = false;
});
}
void CDownloader::stop()
{
m_run = false;
pimpl->m_run = false;
}
wstring CDownloader::GetFilePath()
{
return m_filePath;
return pimpl->m_filePath;
}
void CDownloader::onComplete(FnVoidInt callback)
{
m_complete_callback = callback;
pimpl->m_complete_callback = callback;
}
void CDownloader::onProgress(FnVoidInt callback)
{
m_progress_callback = callback;
pimpl->m_progress_callback = callback;
}

View File

@ -35,12 +35,14 @@
#include <string>
#include <functional>
#include <future>
typedef std::function<void(int)> FnVoidInt;
using std::wstring;
class CDownloaderPrivate;
class CDownloader
{
public:
@ -57,13 +59,7 @@ public:
void onProgress(FnVoidInt callback);
private:
FnVoidInt m_complete_callback = nullptr,
m_progress_callback = nullptr;
wstring m_url,
m_filePath;
std::future<void> m_future;
std::atomic_bool m_run,
m_lock;
CDownloaderPrivate *pimpl = nullptr;
};
#endif // CDOWNLOADER_H