mirror of
https://github.com/ONLYOFFICE/desktop-apps.git
synced 2026-04-07 14:09:22 +08:00
[win] updatesvc: refactoring cdownloader
This commit is contained in:
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
Reference in New Issue
Block a user