From b0adcb5ea6e1557a2ea2db92cc5a7971eb620616 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sun, 4 Jul 2021 19:14:41 +1000 Subject: [PATCH] FrontendCommon: Add HTTPDownloaderUWP --- src/frontend-common/frontend-common.vcxproj | 6 + .../frontend-common.vcxproj.filters | 4 +- src/frontend-common/http_downloader_uwp.cpp | 166 ++++++++++++++++++ src/frontend-common/http_downloader_uwp.h | 37 ++++ 4 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 src/frontend-common/http_downloader_uwp.cpp create mode 100644 src/frontend-common/http_downloader_uwp.h diff --git a/src/frontend-common/frontend-common.vcxproj b/src/frontend-common/frontend-common.vcxproj index cddfd6f8d..dbb02a4a4 100644 --- a/src/frontend-common/frontend-common.vcxproj +++ b/src/frontend-common/frontend-common.vcxproj @@ -18,6 +18,9 @@ + + true + true @@ -67,6 +70,9 @@ + + true + true diff --git a/src/frontend-common/frontend-common.vcxproj.filters b/src/frontend-common/frontend-common.vcxproj.filters index 73d9bff4e..980b960cf 100644 --- a/src/frontend-common/frontend-common.vcxproj.filters +++ b/src/frontend-common/frontend-common.vcxproj.filters @@ -36,6 +36,7 @@ + @@ -73,8 +74,9 @@ + - + \ No newline at end of file diff --git a/src/frontend-common/http_downloader_uwp.cpp b/src/frontend-common/http_downloader_uwp.cpp new file mode 100644 index 000000000..4ea7b64e5 --- /dev/null +++ b/src/frontend-common/http_downloader_uwp.cpp @@ -0,0 +1,166 @@ +#include "http_downloader_uwp.h" +#include "common/assert.h" +#include "common/log.h" +#include "common/string_util.h" +#include "common/timer.h" +#include +Log_SetChannel(HTTPDownloaderWinHttp); + +#include +#include +#include + +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::Web::Http; + +namespace FrontendCommon { + +HTTPDownloaderUWP::HTTPDownloaderUWP(std::string user_agent) : HTTPDownloader(), m_user_agent(std::move(user_agent)) {} + +HTTPDownloaderUWP::~HTTPDownloaderUWP() = default; + +std::unique_ptr HTTPDownloader::Create(const char* user_agent) +{ + std::string user_agent_str; + if (user_agent) + user_agent_str = user_agent; + + return std::make_unique(user_agent ? std::string(user_agent) : std::string()); +} + +HTTPDownloader::Request* HTTPDownloaderUWP::InternalCreateRequest() +{ + Request* req = new Request(); + return req; +} + +void HTTPDownloaderUWP::InternalPollRequests() +{ + // noop - uses async +} + +bool HTTPDownloaderUWP::StartRequest(HTTPDownloader::Request* request) +{ + Request* req = static_cast(request); + + try + { + const std::wstring url_wide(StringUtil::UTF8StringToWideString(req->url)); + const Uri uri(url_wide); + + if (!m_user_agent.empty() && + !req->client.DefaultRequestHeaders().UserAgent().TryParseAdd(StringUtil::UTF8StringToWideString(m_user_agent))) + { + Log_WarningPrintf("Failed to set user agent to '%s'", m_user_agent.c_str()); + } + + if (req->type == Request::Type::Post) + { + const winrt::Windows::Storage::Streams::Buffer post_buf(static_cast(req->post_data.size())); + std::memcpy(post_buf.data(), req->post_data.data(), req->post_data.size()); + + const HttpBufferContent post_content(post_buf); + req->request_async = req->client.PostAsync(uri, post_content); + } + else + { + req->request_async = req->client.GetAsync(uri); + } + + req->request_async.Completed( + [req](const IAsyncOperationWithProgress& operation, AsyncStatus status) { + if (status == AsyncStatus::Completed) + { + Log_DevPrintf("Request for '%s' completed start portion", req->url.c_str()); + try + { + req->state.store(Request::State::Receiving); + req->start_time = Common::Timer::GetValue(); + + const HttpResponseMessage response(req->request_async.get()); + req->status_code = static_cast(response.StatusCode()); + + const IHttpContent content(response.Content()); + req->receive_async = content.ReadAsBufferAsync(); + req->receive_async.Completed( + [req]( + const IAsyncOperationWithProgress& inner_operation, + AsyncStatus inner_status) { + if (inner_status == AsyncStatus::Completed) + { + const winrt::Windows::Storage::Streams::IBuffer buffer(inner_operation.get()); + if (buffer && buffer.Length() > 0) + { + req->data.resize(buffer.Length()); + std::memcpy(req->data.data(), buffer.data(), req->data.size()); + } + + Log_DevPrintf("End of request '%s', %zu bytes received", req->url.c_str(), req->data.size()); + req->state.store(Request::State::Complete); + } + else if (inner_status == AsyncStatus::Canceled) + { + // don't do anything, the request has been freed + } + else + { + Log_ErrorPrintf("Request for '%s' failed during recieve phase: %08X", req->url.c_str(), + inner_operation.ErrorCode().value); + req->status_code = -1; + req->state.store(Request::State::Complete); + } + }); + } + catch (const winrt::hresult_error& err) + { + Log_ErrorPrintf("Failed to receive HTTP request for '%s': %08X %s", req->url.c_str(), err.code(), + StringUtil::WideStringToUTF8String(err.message()).c_str()); + req->status_code = -1; + req->state.store(Request::State::Complete); + } + + req->receive_async = nullptr; + } + else if (status == AsyncStatus::Canceled) + { + // don't do anything, the request has been freed + } + else + { + Log_ErrorPrintf("Request for '%s' failed during start phase: %08X", req->url.c_str(), + operation.ErrorCode().value); + req->status_code = -1; + req->state.store(Request::State::Complete); + } + + req->request_async = nullptr; + }); + } + catch (const winrt::hresult_error& err) + { + Log_ErrorPrintf("Failed to start HTTP request for '%s': %08X %s", req->url.c_str(), err.code(), + StringUtil::WideStringToUTF8String(err.message()).c_str()); + req->callback(-1, req->data); + delete req; + return false; + } + + Log_DevPrintf("Started HTTP request for '%s'", req->url.c_str()); + req->state = Request::State::Started; + req->start_time = Common::Timer::GetValue(); + return true; +} + +void HTTPDownloaderUWP::CloseRequest(HTTPDownloader::Request* request) +{ + Request* req = static_cast(request); + if (req->request_async) + req->request_async.Cancel(); + if (req->receive_async) + req->receive_async.Cancel(); + + req->client.Close(); + delete req; +} + +} // namespace FrontendCommon \ No newline at end of file diff --git a/src/frontend-common/http_downloader_uwp.h b/src/frontend-common/http_downloader_uwp.h new file mode 100644 index 000000000..28d82d9da --- /dev/null +++ b/src/frontend-common/http_downloader_uwp.h @@ -0,0 +1,37 @@ +#pragma once +#include "http_downloader.h" + +#include "common/windows_headers.h" + +#include + +namespace FrontendCommon { + +class HTTPDownloaderUWP final : public HTTPDownloader +{ +public: + HTTPDownloaderUWP(std::string user_agent); + ~HTTPDownloaderUWP() override; + +protected: + Request* InternalCreateRequest() override; + void InternalPollRequests() override; + bool StartRequest(HTTPDownloader::Request* request) override; + void CloseRequest(HTTPDownloader::Request* request) override; + +private: + struct Request : HTTPDownloader::Request + { + std::wstring object_name; + winrt::Windows::Web::Http::HttpClient client; + winrt::Windows::Foundation::IAsyncOperationWithProgress + request_async{nullptr}; + winrt::Windows::Foundation::IAsyncOperationWithProgress + receive_async{}; + }; + + std::string m_user_agent; +}; + +} // namespace FrontendCommon \ No newline at end of file