diff --git a/Common/File/PathBrowser.cpp b/Common/File/PathBrowser.cpp index 5bd0c43e88..116c7cf1b3 100644 --- a/Common/File/PathBrowser.cpp +++ b/Common/File/PathBrowser.cpp @@ -32,7 +32,8 @@ bool LoadRemoteFileList(const std::string &url, bool *cancel, std::vector &responseHeaders, float *progress, bool *cancelled) { +int Client::GET(const char *resource, Buffer *output, std::vector &responseHeaders, RequestProgress *progress) { const char *otherHeaders = "Accept: */*\r\n" "Accept-Encoding: gzip\r\n"; - int err = SendRequest("GET", resource, otherHeaders, progress, cancelled); + int err = SendRequest("GET", resource, otherHeaders, progress); if (err < 0) { return err; } net::Buffer readbuf; - int code = ReadResponseHeaders(&readbuf, responseHeaders, progress, cancelled); + int code = ReadResponseHeaders(&readbuf, responseHeaders, progress); if (code < 0) { return code; } - err = ReadResponseEntity(&readbuf, responseHeaders, output, progress, cancelled); + err = ReadResponseEntity(&readbuf, responseHeaders, output, progress); if (err < 0) { return err; } return code; } -int Client::GET(const char *resource, Buffer *output, float *progress, bool *cancelled) { +int Client::GET(const char *resource, Buffer *output, RequestProgress *progress) { std::vector responseHeaders; - int code = GET(resource, output, responseHeaders, progress, cancelled); + int code = GET(resource, output, responseHeaders, progress); return code; } -int Client::POST(const char *resource, const std::string &data, const std::string &mime, Buffer *output, float *progress) { +int Client::POST(const char *resource, const std::string &data, const std::string &mime, Buffer *output, RequestProgress *progress) { char otherHeaders[2048]; if (mime.empty()) { snprintf(otherHeaders, sizeof(otherHeaders), "Content-Length: %lld\r\n", (long long)data.size()); @@ -297,18 +297,16 @@ int Client::POST(const char *resource, const std::string &data, const std::strin return code; } -int Client::POST(const char *resource, const std::string &data, Buffer *output, float *progress) { +int Client::POST(const char *resource, const std::string &data, Buffer *output, RequestProgress *progress) { return POST(resource, data, "", output, progress); } -int Client::SendRequest(const char *method, const char *resource, const char *otherHeaders, float *progress, bool *cancelled) { - return SendRequestWithData(method, resource, "", otherHeaders, progress, cancelled); +int Client::SendRequest(const char *method, const char *resource, const char *otherHeaders, RequestProgress *progress) { + return SendRequestWithData(method, resource, "", otherHeaders, progress); } -int Client::SendRequestWithData(const char *method, const char *resource, const std::string &data, const char *otherHeaders, float *progress, bool *cancelled) { - if (progress) { - *progress = 0.01f; - } +int Client::SendRequestWithData(const char *method, const char *resource, const std::string &data, const char *otherHeaders, RequestProgress *progress) { + progress->progress = 0.01f; net::Buffer buffer; const char *tpl = @@ -325,20 +323,20 @@ int Client::SendRequestWithData(const char *method, const char *resource, const userAgent_.c_str(), otherHeaders ? otherHeaders : ""); buffer.Append(data); - bool flushed = buffer.FlushSocket(sock(), dataTimeout_); + bool flushed = buffer.FlushSocket(sock(), dataTimeout_, progress->cancelled); if (!flushed) { return -1; // TODO error code. } return 0; } -int Client::ReadResponseHeaders(net::Buffer *readbuf, std::vector &responseHeaders, float *progress, bool *cancelled) { +int Client::ReadResponseHeaders(net::Buffer *readbuf, std::vector &responseHeaders, RequestProgress *progress) { // Snarf all the data we can into RAM. A little unsafe but hey. static constexpr float CANCEL_INTERVAL = 0.25f; bool ready = false; double endTimeout = time_now_d() + dataTimeout_; while (!ready) { - if (cancelled && *cancelled) + if (progress->cancelled && *progress->cancelled) return -1; ready = fd_util::WaitUntilReady(sock(), CANCEL_INTERVAL, false); if (!ready && time_now_d() > endTimeout) { @@ -385,7 +383,7 @@ int Client::ReadResponseHeaders(net::Buffer *readbuf, std::vector & return code; } -int Client::ReadResponseEntity(net::Buffer *readbuf, const std::vector &responseHeaders, Buffer *output, float *progress, bool *cancelled) { +int Client::ReadResponseEntity(net::Buffer *readbuf, const std::vector &responseHeaders, Buffer *output, RequestProgress *progress) { bool gzip = false; bool chunked = false; int contentLength = 0; @@ -417,25 +415,25 @@ int Client::ReadResponseEntity(net::Buffer *readbuf, const std::vectorprogress = 0.1f; } - if (!contentLength || !progress) { + if (!contentLength) { // No way to know how far along we are. Let's just not update the progress counter. - if (!readbuf->ReadAllWithProgress(sock(), contentLength, nullptr, cancelled)) + if (!readbuf->ReadAllWithProgress(sock(), contentLength, nullptr, progress->cancelled)) return -1; } else { // Let's read in chunks, updating progress between each. - if (!readbuf->ReadAllWithProgress(sock(), contentLength, progress, cancelled)) + if (!readbuf->ReadAllWithProgress(sock(), contentLength, &progress->progress, progress->cancelled)) return -1; } // output now contains the rest of the reply. Dechunk it. if (chunked) { - DeChunk(readbuf, output, contentLength, progress); + DeChunk(readbuf, output, contentLength, &progress->progress); } else { output->Append(*readbuf); } @@ -447,21 +445,18 @@ int Client::ReadResponseEntity(net::Buffer *readbuf, const std::vectorprogress = 0.0f; return -1; } output->Append(decompressed); } - if (progress) { - *progress = 1.0f; - } + progress->progress = 1.0f; return 0; } Download::Download(const std::string &url, const std::string &outfile) - : url_(url), outfile_(outfile) { + : progress_(&cancelled_), url_(url), outfile_(outfile) { } Download::~Download() { @@ -482,7 +477,7 @@ void Download::Join() { void Download::SetFailed(int code) { failed_ = true; - progress_ = 1.0f; + progress_.progress = 1.0f; completed_ = true; } @@ -511,7 +506,7 @@ int Download::PerformGET(const std::string &url) { return -1; } - return client.GET(fileUrl.Resource().c_str(), &buffer_, responseHeaders_, &progress_, &cancelled_); + return client.GET(fileUrl.Resource().c_str(), &buffer_, responseHeaders_, &progress_); } std::string Download::RedirectLocation(const std::string &baseUrl) { @@ -565,7 +560,7 @@ void Download::Do() { resultCode_ = resultCode; } - progress_ = 1.0f; + progress_.progress = 1.0f; // Set this last to ensure no race conditions when checking Done. Users must always check // Done before looking at the result code. diff --git a/Common/Net/HTTPClient.h b/Common/Net/HTTPClient.h index 2cc8901f05..99da33b5dc 100644 --- a/Common/Net/HTTPClient.h +++ b/Common/Net/HTTPClient.h @@ -43,26 +43,34 @@ namespace http { bool GetHeaderValue(const std::vector &responseHeaders, const std::string &header, std::string *value); +struct RequestProgress { + RequestProgress() {} + explicit RequestProgress(bool *c) : cancelled(c) {} + + float progress = 0.0f; + bool *cancelled = nullptr; +}; + class Client : public net::Connection { public: Client(); ~Client(); // Return value is the HTTP return code. 200 means OK. < 0 means some local error. - int GET(const char *resource, Buffer *output, float *progress = nullptr, bool *cancelled = nullptr); - int GET(const char *resource, Buffer *output, std::vector &responseHeaders, float *progress = nullptr, bool *cancelled = nullptr); + int GET(const char *resource, Buffer *output, RequestProgress *progress); + int GET(const char *resource, Buffer *output, std::vector &responseHeaders, RequestProgress *progress); // Return value is the HTTP return code. - int POST(const char *resource, const std::string &data, const std::string &mime, Buffer *output, float *progress = nullptr); - int POST(const char *resource, const std::string &data, Buffer *output, float *progress = nullptr); + int POST(const char *resource, const std::string &data, const std::string &mime, Buffer *output, RequestProgress *progress); + int POST(const char *resource, const std::string &data, Buffer *output, RequestProgress *progress); // HEAD, PUT, DELETE aren't implemented yet, but can be done with SendRequest. - int SendRequest(const char *method, const char *resource, const char *otherHeaders = nullptr, float *progress = nullptr, bool *cancelled = nullptr); - int SendRequestWithData(const char *method, const char *resource, const std::string &data, const char *otherHeaders = nullptr, float *progress = nullptr, bool *cancelled = nullptr); - int ReadResponseHeaders(net::Buffer *readbuf, std::vector &responseHeaders, float *progress = nullptr, bool *cancelled = nullptr); + int SendRequest(const char *method, const char *resource, const char *otherHeaders, RequestProgress *progress); + int SendRequestWithData(const char *method, const char *resource, const std::string &data, const char *otherHeaders, RequestProgress *progress); + int ReadResponseHeaders(net::Buffer *readbuf, std::vector &responseHeaders, RequestProgress *progress); // If your response contains a response, you must read it. - int ReadResponseEntity(net::Buffer *readbuf, const std::vector &responseHeaders, Buffer *output, float *progress = nullptr, bool *cancelled = nullptr); + int ReadResponseEntity(net::Buffer *readbuf, const std::vector &responseHeaders, Buffer *output, RequestProgress *progress); void SetDataTimeout(double t) { dataTimeout_ = t; @@ -89,7 +97,7 @@ public: void Join(); // Returns 1.0 when done. That one value can be compared exactly - or just use Done(). - float Progress() const { return progress_; } + float Progress() const { return progress_.progress; } bool Done() const { return completed_; } bool Failed() const { return failed_; } @@ -133,7 +141,7 @@ private: int PerformGET(const std::string &url); std::string RedirectLocation(const std::string &baseUrl); void SetFailed(int code); - float progress_ = 0.0f; + RequestProgress progress_; Buffer buffer_; std::vector responseHeaders_; std::string url_; diff --git a/Common/Net/NetBuffer.cpp b/Common/Net/NetBuffer.cpp index b201c2dfc3..baf903acc5 100644 --- a/Common/Net/NetBuffer.cpp +++ b/Common/Net/NetBuffer.cpp @@ -50,6 +50,7 @@ bool Buffer::FlushSocket(uintptr_t sock, double timeout, bool *cancelled) { bool Buffer::ReadAllWithProgress(int fd, int knownSize, float *progress, bool *cancelled) { static constexpr float CANCEL_INTERVAL = 0.25f; std::vector buf; + // We're non-blocking and reading from an OS buffer, so try to read as much as we can at a time. if (knownSize >= 65536 * 16) { buf.resize(65536); } else if (knownSize >= 1024 * 16) { diff --git a/Core/FileLoaders/HTTPFileLoader.cpp b/Core/FileLoaders/HTTPFileLoader.cpp index 0535ee024a..5f385910a4 100644 --- a/Core/FileLoaders/HTTPFileLoader.cpp +++ b/Core/FileLoaders/HTTPFileLoader.cpp @@ -24,7 +24,7 @@ #include "Core/FileLoaders/HTTPFileLoader.h" HTTPFileLoader::HTTPFileLoader(const std::string &filename) - : url_(filename), filename_(filename) { + : url_(filename), progress_(&cancel_), filename_(filename) { } void HTTPFileLoader::Prepare() { @@ -137,7 +137,7 @@ int HTTPFileLoader::SendHEAD(const Url &url, std::vector &responseH return -400; } - int err = client_.SendRequest("HEAD", url.Resource().c_str()); + int err = client_.SendRequest("HEAD", url.Resource().c_str(), nullptr, &progress_); if (err < 0) { ERROR_LOG(LOADER, "HTTP request failed, failed to send request: %s port %d", url.Host().c_str(), url.Port()); latestError_ = "Could not connect (could not request data)"; @@ -146,7 +146,7 @@ int HTTPFileLoader::SendHEAD(const Url &url, std::vector &responseH } net::Buffer readbuf; - return client_.ReadResponseHeaders(&readbuf, responseHeaders); + return client_.ReadResponseHeaders(&readbuf, responseHeaders, &progress_); } HTTPFileLoader::~HTTPFileLoader() { @@ -205,7 +205,7 @@ size_t HTTPFileLoader::ReadAt(s64 absolutePos, size_t bytes, void *data, Flags f net::Buffer readbuf; std::vector responseHeaders; - int code = client_.ReadResponseHeaders(&readbuf, responseHeaders); + int code = client_.ReadResponseHeaders(&readbuf, responseHeaders, &progress_); if (code != 206) { ERROR_LOG(LOADER, "HTTP server did not respond with range, received code=%03d", code); latestError_ = "Invalid response reading data"; @@ -236,7 +236,7 @@ size_t HTTPFileLoader::ReadAt(s64 absolutePos, size_t bytes, void *data, Flags f // TODO: Would be nice to read directly. net::Buffer output; - int res = client_.ReadResponseEntity(&readbuf, responseHeaders, &output); + int res = client_.ReadResponseEntity(&readbuf, responseHeaders, &output, &progress_); if (res != 0) { ERROR_LOG(LOADER, "Unable to read HTTP response entity: %d", res); // Let's take anything we got anyway. Not worse than returning nothing? @@ -259,8 +259,8 @@ size_t HTTPFileLoader::ReadAt(s64 absolutePos, size_t bytes, void *data, Flags f void HTTPFileLoader::Connect() { if (!connected_) { - cancelConnect_ = false; + cancel_ = false; // Latency is important here, so reduce the timeout. - connected_ = client_.Connect(3, 10.0, &cancelConnect_); + connected_ = client_.Connect(3, 10.0, &cancel_); } } diff --git a/Core/FileLoaders/HTTPFileLoader.h b/Core/FileLoaders/HTTPFileLoader.h index d8cb6f3c47..6bbba43eff 100644 --- a/Core/FileLoaders/HTTPFileLoader.h +++ b/Core/FileLoaders/HTTPFileLoader.h @@ -46,7 +46,7 @@ public: virtual size_t ReadAt(s64 absolutePos, size_t bytes, void *data, Flags flags = Flags::NONE) override; void Cancel() override { - cancelConnect_ = true; + cancel_ = true; } std::string LatestError() const override { @@ -70,9 +70,10 @@ private: s64 filepos_ = 0; Url url_; http::Client client_; + http::RequestProgress progress_; std::string filename_; bool connected_ = false; - bool cancelConnect_ = false; + bool cancel_ = false; const char *latestError_ = ""; std::once_flag preparedFlag_; diff --git a/Core/Reporting.cpp b/Core/Reporting.cpp index 3b7cb2d31d..e00e56730f 100644 --- a/Core/Reporting.cpp +++ b/Core/Reporting.cpp @@ -261,6 +261,7 @@ namespace Reporting bool SendReportRequest(const char *uri, const std::string &data, const std::string &mimeType, Buffer *output = NULL) { http::Client http; + http::RequestProgress progress; Buffer theVoid; http.SetUserAgent(StringFromFormat("PPSSPP/%s", PPSSPP_GIT_VERSION)); @@ -274,7 +275,7 @@ namespace Reporting if (http.Resolve(serverHost, ServerPort())) { http.Connect(); - int result = http.POST(uri, data, mimeType, output); + int result = http.POST(uri, data, mimeType, output, &progress); http.Disconnect(); return result >= 200 && result < 300; diff --git a/Core/WebServer.cpp b/Core/WebServer.cpp index 3efeedc020..35ea2ff48e 100644 --- a/Core/WebServer.cpp +++ b/Core/WebServer.cpp @@ -63,6 +63,7 @@ static ServerStatus RetrieveStatus() { static bool RegisterServer(int port) { bool success = false; http::Client http; + http::RequestProgress progress; Buffer theVoid; http.SetUserAgent(StringFromFormat("PPSSPP/%s", PPSSPP_GIT_VERSION)); @@ -73,7 +74,7 @@ static bool RegisterServer(int port) { std::string ip = fd_util::GetLocalIP(http.sock()); snprintf(resource4, sizeof(resource4) - 1, "/match/update?local=%s&port=%d", ip.c_str(), port); - if (http.GET(resource4, &theVoid) > 0) + if (http.GET(resource4, &theVoid, &progress) > 0) success = true; theVoid.Skip(theVoid.size()); http.Disconnect(); @@ -86,7 +87,7 @@ static bool RegisterServer(int port) { // We register both IPv4 and IPv6 in case the other client is using a different one. if (resource4[0] != 0 && http.Connect(timeout)) { - if (http.GET(resource4, &theVoid) > 0) + if (http.GET(resource4, &theVoid, &progress) > 0) success = true; theVoid.Skip(theVoid.size()); http.Disconnect(); @@ -98,7 +99,7 @@ static bool RegisterServer(int port) { std::string ip = fd_util::GetLocalIP(http.sock()); snprintf(resource6, sizeof(resource6) - 1, "/match/update?local=%s&port=%d", ip.c_str(), port); - if (http.GET(resource6, &theVoid) > 0) + if (http.GET(resource6, &theVoid, &progress) > 0) success = true; theVoid.Skip(theVoid.size()); http.Disconnect(); diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index 8a61fad262..1efb63ee3e 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -136,7 +136,8 @@ bool RemoteISOConnectScreen::FindServer(std::string &resultHost, int &resultPort } SetStatus("Loading game list from [URL]...", host, port); - code = http.GET(subdir.c_str(), &result); + http::RequestProgress progress(&scanCancelled); + code = http.GET(subdir.c_str(), &result, &progress); http.Disconnect(); if (code != 200) { @@ -189,7 +190,8 @@ bool RemoteISOConnectScreen::FindServer(std::string &resultHost, int &resultPort SetStatus("Looking for peers...", "", 0); if (http.Resolve(REPORT_HOSTNAME, REPORT_PORT)) { if (http.Connect(2, 20.0, &scanCancelled)) { - code = http.GET("/match/list", &result); + http::RequestProgress progress(&scanCancelled); + code = http.GET("/match/list", &result, &progress); http.Disconnect(); } }