mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-03-04 20:27:57 +00:00
http: Always use/require progress in requests.
This fixes several cases where we weren't passing cancel flags consistently.
This commit is contained in:
parent
f762fbc53d
commit
3379f33882
@ -32,7 +32,8 @@ bool LoadRemoteFileList(const std::string &url, bool *cancel, std::vector<File::
|
||||
// Start by requesting the list of files from the server.
|
||||
if (http.Resolve(baseURL.Host().c_str(), baseURL.Port())) {
|
||||
if (http.Connect(2, 20.0, cancel)) {
|
||||
code = http.GET(baseURL.Resource().c_str(), &result, responseHeaders);
|
||||
http::RequestProgress progress(cancel);
|
||||
code = http.GET(baseURL.Resource().c_str(), &result, responseHeaders, &progress);
|
||||
http.Disconnect();
|
||||
}
|
||||
}
|
||||
|
@ -243,35 +243,35 @@ void DeChunk(Buffer *inbuffer, Buffer *outbuffer, int contentLength, float *prog
|
||||
}
|
||||
}
|
||||
|
||||
int Client::GET(const char *resource, Buffer *output, std::vector<std::string> &responseHeaders, float *progress, bool *cancelled) {
|
||||
int Client::GET(const char *resource, Buffer *output, std::vector<std::string> &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<std::string> 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<std::string> &responseHeaders, float *progress, bool *cancelled) {
|
||||
int Client::ReadResponseHeaders(net::Buffer *readbuf, std::vector<std::string> &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<std::string> &
|
||||
return code;
|
||||
}
|
||||
|
||||
int Client::ReadResponseEntity(net::Buffer *readbuf, const std::vector<std::string> &responseHeaders, Buffer *output, float *progress, bool *cancelled) {
|
||||
int Client::ReadResponseEntity(net::Buffer *readbuf, const std::vector<std::string> &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::vector<std::stri
|
||||
contentLength = 0;
|
||||
}
|
||||
|
||||
if (!contentLength && progress) {
|
||||
if (!contentLength) {
|
||||
// Content length is unknown.
|
||||
// Set progress to 1% so it looks like something is happening...
|
||||
*progress = 0.1f;
|
||||
progress->progress = 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::vector<std::stri
|
||||
bool result = decompress_string(compressed, &decompressed);
|
||||
if (!result) {
|
||||
ERROR_LOG(IO, "Error decompressing using zlib");
|
||||
if (progress)
|
||||
*progress = 0.0f;
|
||||
progress->progress = 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.
|
||||
|
@ -43,26 +43,34 @@ namespace http {
|
||||
|
||||
bool GetHeaderValue(const std::vector<std::string> &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<std::string> &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<std::string> &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<std::string> &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<std::string> &responseHeaders, RequestProgress *progress);
|
||||
// If your response contains a response, you must read it.
|
||||
int ReadResponseEntity(net::Buffer *readbuf, const std::vector<std::string> &responseHeaders, Buffer *output, float *progress = nullptr, bool *cancelled = nullptr);
|
||||
int ReadResponseEntity(net::Buffer *readbuf, const std::vector<std::string> &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<std::string> responseHeaders_;
|
||||
std::string url_;
|
||||
|
@ -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<char> 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) {
|
||||
|
@ -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<std::string> &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<std::string> &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<std::string> 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_);
|
||||
}
|
||||
}
|
||||
|
@ -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_;
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user