diff --git a/Common/Net/HTTPClient.cpp b/Common/Net/HTTPClient.cpp index b5c47f6695..5638e30529 100644 --- a/Common/Net/HTTPClient.cpp +++ b/Common/Net/HTTPClient.cpp @@ -542,10 +542,9 @@ std::string HTTPDownload::RedirectLocation(const std::string &baseUrl) { } void HTTPDownload::Do() { - SetCurrentThreadName("Downloader::Do"); + SetCurrentThreadName("HTTPDownload::Do"); AndroidJNIThreadContext jniContext; - resultCode_ = 0; std::string downloadURL = url_; diff --git a/Common/Net/HTTPClient.h b/Common/Net/HTTPClient.h index 10113c3a9c..85543bb53c 100644 --- a/Common/Net/HTTPClient.h +++ b/Common/Net/HTTPClient.h @@ -107,7 +107,7 @@ public: void Start() override; void Join() override; - bool Done() const override { return completed_; } + bool Done() override { return completed_; } bool Failed() const override { return failed_; } // NOTE! The value of ResultCode is INVALID until Done() returns true. diff --git a/Common/Net/HTTPNaettRequest.cpp b/Common/Net/HTTPNaettRequest.cpp index 0d1a373d4f..dd0539cc39 100644 --- a/Common/Net/HTTPNaettRequest.cpp +++ b/Common/Net/HTTPNaettRequest.cpp @@ -1,5 +1,92 @@ +#include + #include "Common/Net/HTTPRequest.h" #include "Common/Net/HTTPNaettRequest.h" +#include "Common/Thread/ThreadUtil.h" +#include "Common/Log.h" #include "ext/naett/naett.h" +namespace http { + +HTTPSDownload::HTTPSDownload(RequestMethod method, const std::string &url, const std::string &postData, const std::string &postMime, const Path &outfile, ProgressBarMode progressBarMode, const std::string &name) + : Download(url, name, progress_.cancelled), method_(method), postData_(postData), postMime_(postMime), outfile_(outfile), progressBarMode_(progressBarMode) { +} + +HTTPSDownload::~HTTPSDownload() { + Join(); +} + +void HTTPSDownload::Start() { + _dbg_assert_(!req_); + + const char *methodStr = method_ == RequestMethod::GET ? "GET" : "POST"; + req_ = naettRequest_va(url_.c_str(), !postMime_.empty() ? 3 : 2, naettMethod(methodStr), naettHeader("accept", "application/json, text/*; q=0.9, */*; q=0.8"), naettHeader("Content-Type", postMime_.c_str())); + res_ = naettMake(req_); +} + +void HTTPSDownload::Join() { + if (!res_ || !req_) + return; // No pending operation. + // Tear down. + if (completed_ && res_) { + _dbg_assert_(req_); + naettClose(res_); + naettFree(req_); + res_ = nullptr; + req_ = nullptr; + } else { + ERROR_LOG(IO, "HTTPSDownload::Join not implemented"); + } +} + +bool HTTPSDownload::Done() { + if (completed_) + return true; + + if (naettComplete(res_)) { + resultCode_ = naettGetStatus(res_); + if (resultCode_ < 0) { + // It's a naett error. Translate and handle. + switch (resultCode_) { + case naettConnectionError: // -1 + ERROR_LOG(IO, "Connection error"); + break; + case naettProtocolError: // -2 + ERROR_LOG(IO, "Protocol error"); + break; + case naettReadError: // -3 + ERROR_LOG(IO, "Read error"); + break; + case naettWriteError: // -4 + ERROR_LOG(IO, "Write error"); + break; + case naettGenericError: // -4 + ERROR_LOG(IO, "Generic error"); + break; + } + } else if (resultCode_ == 200) { + int bodyLength; + const void *body = naettGetBody(res_, &bodyLength); + char *dest = buffer_.Append(bodyLength); + memcpy(dest, body, bodyLength); + + if (!outfile_.empty() && !buffer_.FlushToFile(outfile_)) { + ERROR_LOG(IO, "Failed writing download to '%s'", outfile_.c_str()); + } + } else { + WARN_LOG(IO, "Naett request failed: %d", resultCode_); + failed_ = true; + } + + completed_ = true; + + if (callback_) + callback_(*this); + return true; + } + + return false; +} + +} // namespace diff --git a/Common/Net/HTTPNaettRequest.h b/Common/Net/HTTPNaettRequest.h index bb724b5c6e..3dd557086e 100644 --- a/Common/Net/HTTPNaettRequest.h +++ b/Common/Net/HTTPNaettRequest.h @@ -1,3 +1,68 @@ #pragma once #include "Common/Net/HTTPRequest.h" +#include "ext/naett/naett.h" +#include + +namespace http { + +// Really an asynchronous request. +class HTTPSDownload : public Download { +public: + HTTPSDownload(RequestMethod method, const std::string &url, const std::string &postData, const std::string &postMime, const Path &outfile, ProgressBarMode progressBarMode = ProgressBarMode::DELAYED, const std::string &name = ""); + ~HTTPSDownload(); + + void SetAccept(const char *mime) override { + acceptMime_ = mime; + } + + void SetUserAgent(const std::string &userAgent) override { + userAgent_ = userAgent; + } + + void Start() override; + void Join() override; + + // Also acts as a Poll. + bool Done() override; + bool Failed() const override { return failed_; } + + // NOTE! The value of ResultCode is INVALID until Done() returns true. + int ResultCode() const override { return resultCode_; } + + const Path &outfile() const override { return outfile_; } + + // If not downloading to a file, access this to get the result. + Buffer &buffer() override { return buffer_; } + const Buffer &buffer() const override { return buffer_; } + + void Cancel() override { + cancelled_ = true; + } + + bool IsCancelled() const override { + return cancelled_; + } + +private: + RequestMethod method_; + std::string postData_; + std::string userAgent_; + Buffer buffer_; + std::vector responseHeaders_; + Path outfile_; + const char *acceptMime_ = "*/*"; + std::string postMime_; + int resultCode_ = 0; + bool completed_ = false; + bool failed_ = false; + bool cancelled_ = false; + ProgressBarMode progressBarMode_; + bool joined_ = false; + + // Naett state + naettReq *req_ = nullptr; + naettRes *res_ = nullptr; +}; + +} // namespace http diff --git a/Common/Net/HTTPRequest.cpp b/Common/Net/HTTPRequest.cpp index c993bd4566..988f81b190 100644 --- a/Common/Net/HTTPRequest.cpp +++ b/Common/Net/HTTPRequest.cpp @@ -1,11 +1,22 @@ #include "Common/Net/HTTPRequest.h" #include "Common/Net/HTTPClient.h" +#include "Common/Net/HTTPNaettRequest.h" #include "Common/TimeUtil.h" +#include "Common/StringUtils.h" namespace http { +bool RequestManager::IsHttpsUrl(const std::string &url) { + return startsWith(url, "https:"); +} + std::shared_ptr RequestManager::StartDownload(const std::string &url, const Path &outfile, ProgressBarMode mode, const char *acceptMime) { - std::shared_ptr dl(new HTTPDownload(RequestMethod::GET, url, "", "", outfile, mode)); + std::shared_ptr dl; + if (IsHttpsUrl(url)) { + dl.reset(new HTTPSDownload(RequestMethod::GET, url, "", "", outfile, mode)); + } else { + dl.reset(new HTTPDownload(RequestMethod::GET, url, "", "", outfile, mode)); + } if (!userAgent_.empty()) dl->SetUserAgent(userAgent_); @@ -23,7 +34,12 @@ std::shared_ptr RequestManager::StartDownloadWithCallback( std::function callback, const std::string &name, const char *acceptMime) { - std::shared_ptr dl(new HTTPDownload(RequestMethod::GET, url, "", "", outfile, mode, name)); + std::shared_ptr dl; + if (IsHttpsUrl(url)) { + dl.reset(new HTTPSDownload(RequestMethod::GET, url, "", "", outfile, mode, name)); + } else { + dl.reset(new HTTPDownload(RequestMethod::GET, url, "", "", outfile, mode, name)); + } if (!userAgent_.empty()) dl->SetUserAgent(userAgent_); if (acceptMime) @@ -41,7 +57,12 @@ std::shared_ptr RequestManager::AsyncPostWithCallback( ProgressBarMode mode, std::function callback, const std::string &name) { - std::shared_ptr dl(new HTTPDownload(RequestMethod::POST, url, postData, postMime, Path(), mode, name)); + std::shared_ptr dl; + if (IsHttpsUrl(url)) { + dl.reset(new HTTPSDownload(RequestMethod::POST, url, postData, postMime, Path(), mode, name)); + } else { + dl.reset(new HTTPDownload(RequestMethod::POST, url, postData, postMime, Path(), mode, name)); + } if (!userAgent_.empty()) dl->SetUserAgent(userAgent_); dl->SetCallback(callback); diff --git a/Common/Net/HTTPRequest.h b/Common/Net/HTTPRequest.h index ae09baa9ce..7c55c5bd85 100644 --- a/Common/Net/HTTPRequest.h +++ b/Common/Net/HTTPRequest.h @@ -43,7 +43,7 @@ public: virtual void Start() = 0; virtual void Join() = 0; - virtual bool Done() const = 0; + virtual bool Done() = 0; virtual bool Failed() const = 0; virtual int ResultCode() const = 0; @@ -106,6 +106,8 @@ public: } private: + bool IsHttpsUrl(const std::string &url); + std::vector> downloads_; // These get copied to downloads_ in Update(). It's so that callbacks can add new downloads // while running. diff --git a/Common/Net/Resolve.cpp b/Common/Net/Resolve.cpp index 76f0055f50..441905777d 100644 --- a/Common/Net/Resolve.cpp +++ b/Common/Net/Resolve.cpp @@ -30,8 +30,11 @@ #include "Common/Log.h" #include "Common/TimeUtil.h" +#include "ext/naett/naett.h" + namespace net { +int g_refCount = 0; void Init() { @@ -40,6 +43,10 @@ void Init() WSADATA wsaData = {0}; WSAStartup(MAKEWORD(2, 2), &wsaData); #endif + if (g_refCount == 0) { + naettInit(NULL); + } + g_refCount++; } void Shutdown() diff --git a/Common/System/System.h b/Common/System/System.h index fd1ca445a5..17eeda194d 100644 --- a/Common/System/System.h +++ b/Common/System/System.h @@ -136,6 +136,8 @@ enum SystemProperty { SYSPROP_CAN_CREATE_SHORTCUT, + SYSPROP_SUPPORTS_HTTPS, + // Available as Int: SYSPROP_SYSTEMVERSION, SYSPROP_DISPLAY_XRES, diff --git a/Core/Config.cpp b/Core/Config.cpp index 316157d3e5..70eb254f2e 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -57,7 +57,7 @@ #include "GPU/Common/FramebufferManagerCommon.h" // TODO: Find a better place for this. -http::Downloader g_DownloadManager; +http::RequestManager g_DownloadManager; Config g_Config; @@ -1174,7 +1174,7 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) { if (iRunCount % 10 == 0 && bCheckForNewVersion) { const char *versionUrl = "http://www.ppsspp.org/version.json"; const char *acceptMime = "application/json, text/*; q=0.9, */*; q=0.8"; - g_DownloadManager.StartDownloadWithCallback(versionUrl, Path(), http::ProgressBarMode::NONE, &DownloadCompletedCallback, acceptMime); + g_DownloadManager.StartDownloadWithCallback(versionUrl, Path(), http::ProgressBarMode::NONE, &DownloadCompletedCallback, "version", acceptMime); } INFO_LOG(LOADER, "Loading controller config: %s", controllerIniFilename_.c_str()); diff --git a/Core/Config.h b/Core/Config.h index bd1b4223a1..98edbd35b5 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -42,7 +42,7 @@ enum ChatPositions { namespace http { class Download; - class Downloader; + class RequestManager; } struct UrlEncoder; @@ -595,6 +595,6 @@ private: std::string CreateRandMAC(); // TODO: Find a better place for this. -extern http::Downloader g_DownloadManager; +extern http::RequestManager g_DownloadManager; extern Config g_Config; diff --git a/UI/Store.cpp b/UI/Store.cpp index 0718ad2882..b8fe72bc2a 100644 --- a/UI/Store.cpp +++ b/UI/Store.cpp @@ -35,7 +35,7 @@ #include "UI/EmuScreen.h" #include "UI/Store.h" -const std::string storeBaseUrl = "http://store.ppsspp.org/"; +const std::string storeBaseUrl = "https://store.ppsspp.org/"; // baseUrl is assumed to have a trailing slash, and not contain any subdirectories. std::string ResolveUrl(std::string baseUrl, std::string url) { @@ -53,12 +53,12 @@ std::string ResolveUrl(std::string baseUrl, std::string url) { class HttpImageFileView : public UI::View { public: - HttpImageFileView(http::Downloader *downloader, const std::string &path, UI::ImageSizeMode sizeMode = UI::IS_DEFAULT, bool useIconCache = true, UI::LayoutParams *layoutParams = nullptr) - : UI::View(layoutParams), path_(path), sizeMode_(sizeMode), downloader_(downloader), useIconCache_(useIconCache) { + HttpImageFileView(http::RequestManager *requestManager, const std::string &path, UI::ImageSizeMode sizeMode = UI::IS_DEFAULT, bool useIconCache = true, UI::LayoutParams *layoutParams = nullptr) + : UI::View(layoutParams), path_(path), sizeMode_(sizeMode), requestManager_(requestManager), useIconCache_(useIconCache) { if (useIconCache && g_iconCache.MarkPending(path_)) { const char *acceptMime = "image/png, image/jpeg, image/*; q=0.9, */*; q=0.8"; - downloader_->StartDownloadWithCallback(path_, Path(), http::ProgressBarMode::DELAYED, [&](http::Download &download) { + requestManager_->StartDownloadWithCallback(path_, Path(), http::ProgressBarMode::DELAYED, [&](http::Download &download) { if (download.ResultCode() == 200) { std::string data; download.buffer().TakeAll(&data); @@ -100,7 +100,7 @@ private: std::string path_; // or cache key uint32_t color_ = 0xFFFFFFFF; UI::ImageSizeMode sizeMode_; - http::Downloader *downloader_; + http::RequestManager *requestManager_; std::shared_ptr download_; std::string textureData_; @@ -169,7 +169,7 @@ void HttpImageFileView::Draw(UIContext &dc) { if (!texture_ && !textureFailed_ && !path_.empty() && !download_) { auto cb = std::bind(&HttpImageFileView::DownloadCompletedCallback, this, std::placeholders::_1); const char *acceptMime = "image/png, image/jpeg, image/*; q=0.9, */*; q=0.8"; - downloader_->StartDownloadWithCallback(path_, Path(), http::ProgressBarMode::NONE, cb, acceptMime); + requestManager_->StartDownloadWithCallback(path_, Path(), http::ProgressBarMode::NONE, cb, acceptMime); } if (!textureData_.empty()) { diff --git a/Windows/main.cpp b/Windows/main.cpp index 7fe8de2ddc..6cbd2e6243 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -375,6 +375,8 @@ bool System_GetPropertyBool(SystemProperty prop) { return true; case SYSPROP_SUPPORTS_OPEN_FILE_IN_EDITOR: return true; // FileUtil.cpp: OpenFileInEditor + case SYSPROP_SUPPORTS_HTTPS: + return true; default: return false; }