mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-14 02:55:15 +02:00
140 lines
3.9 KiB
C++
140 lines
3.9 KiB
C++
#include "Common/Net/HTTPRequest.h"
|
|
#include "Common/Net/HTTPClient.h"
|
|
#include "Common/Net/URL.h"
|
|
#include "Common/System/OSD.h"
|
|
#include "Common/Thread/ThreadUtil.h"
|
|
|
|
namespace http {
|
|
|
|
HTTPRequest::HTTPRequest(RequestMethod method, std::string_view url, std::string_view postData, std::string_view postMime, const Path &outfile, RequestFlags flags, net::ResolveFunc customResolve, std::string_view name)
|
|
: Request(method, url, name, &cancelled_, flags), postData_(postData), postMime_(postMime), customResolve_(customResolve) {
|
|
outfile_ = outfile;
|
|
}
|
|
|
|
HTTPRequest::~HTTPRequest() {
|
|
g_OSD.RemoveProgressBar(url_, !failed_, 0.5f);
|
|
|
|
if (thread_.joinable()) {
|
|
_dbg_assert_msg_(false, "Download destructed without join");
|
|
}
|
|
}
|
|
|
|
void HTTPRequest::Start() {
|
|
thread_ = std::thread([this] { Do(); });
|
|
}
|
|
|
|
void HTTPRequest::Join() {
|
|
if (!thread_.joinable()) {
|
|
ERROR_LOG(Log::HTTP, "Already joined thread!");
|
|
_dbg_assert_(false);
|
|
}
|
|
thread_.join();
|
|
}
|
|
|
|
void HTTPRequest::SetFailed(int code) {
|
|
failed_ = true;
|
|
progress_.Update(0, 0, true);
|
|
completed_ = true;
|
|
}
|
|
|
|
int HTTPRequest::Perform(const std::string &url) {
|
|
Url fileUrl(url);
|
|
if (!fileUrl.Valid()) {
|
|
return -1;
|
|
}
|
|
|
|
http::Client client(customResolve_);
|
|
if (!userAgent_.empty()) {
|
|
client.SetUserAgent(userAgent_);
|
|
}
|
|
|
|
if (!client.Resolve(fileUrl.Host().c_str(), fileUrl.Port())) {
|
|
ERROR_LOG(Log::HTTP, "Failed resolving %s", url.c_str());
|
|
return -1;
|
|
}
|
|
|
|
if (cancelled_) {
|
|
return -1;
|
|
}
|
|
|
|
if (!client.Connect(2, 20.0, &cancelled_)) {
|
|
ERROR_LOG(Log::HTTP, "Failed connecting to server or cancelled (=%d).", cancelled_);
|
|
return -1;
|
|
}
|
|
|
|
if (cancelled_) {
|
|
return -1;
|
|
}
|
|
|
|
RequestParams req(fileUrl.Resource(), acceptMime_);
|
|
if (method_ == RequestMethod::GET) {
|
|
return client.GET(req, &buffer_, responseHeaders_, &progress_);
|
|
} else {
|
|
return client.POST(req, postData_, postMime_, &buffer_, &progress_);
|
|
}
|
|
}
|
|
|
|
std::string HTTPRequest::RedirectLocation(const std::string &baseUrl) const {
|
|
std::string redirectUrl;
|
|
if (GetHeaderValue(responseHeaders_, "Location", &redirectUrl)) {
|
|
Url url(baseUrl);
|
|
url = url.Relative(redirectUrl);
|
|
redirectUrl = url.ToString();
|
|
}
|
|
return redirectUrl;
|
|
}
|
|
|
|
void HTTPRequest::Do() {
|
|
SetCurrentThreadName("HTTPDownload::Do");
|
|
|
|
AndroidJNIThreadContext jniContext;
|
|
resultCode_ = 0;
|
|
|
|
std::string downloadURL = url_;
|
|
while (resultCode_ == 0) {
|
|
// This is where the new request is performed.
|
|
int resultCode = Perform(downloadURL);
|
|
if (resultCode == -1) {
|
|
SetFailed(resultCode);
|
|
return;
|
|
}
|
|
|
|
if (resultCode == 301 || resultCode == 302 || resultCode == 303 || resultCode == 307 || resultCode == 308) {
|
|
std::string redirectURL = RedirectLocation(downloadURL);
|
|
if (redirectURL.empty()) {
|
|
ERROR_LOG(Log::HTTP, "Could not find Location header for redirect");
|
|
resultCode_ = resultCode;
|
|
} else if (redirectURL == downloadURL || redirectURL == url_) {
|
|
// Simple loop detected, bail out.
|
|
resultCode_ = resultCode;
|
|
}
|
|
|
|
// Perform the next GET.
|
|
if (resultCode_ == 0) {
|
|
INFO_LOG(Log::HTTP, "Download of %s redirected to %s", downloadURL.c_str(), redirectURL.c_str());
|
|
buffer_.clear();
|
|
responseHeaders_.clear();
|
|
}
|
|
downloadURL = redirectURL;
|
|
continue;
|
|
}
|
|
|
|
if (resultCode == 200) {
|
|
INFO_LOG(Log::HTTP, "Completed requesting %s (storing result to %s)", url_.c_str(), outfile_.empty() ? "memory" : outfile_.c_str());
|
|
bool clear = !(flags_ & RequestFlags::KeepInMemory);
|
|
if (!outfile_.empty() && !buffer_.FlushToFile(outfile_, clear)) {
|
|
ERROR_LOG(Log::HTTP, "Failed writing download to '%s'", outfile_.c_str());
|
|
}
|
|
} else {
|
|
ERROR_LOG(Log::HTTP, "Error requesting '%s' (storing result to '%s'): %i", url_.c_str(), outfile_.empty() ? "memory" : outfile_.c_str(), resultCode);
|
|
}
|
|
resultCode_ = resultCode;
|
|
}
|
|
|
|
// Set this last to ensure no race conditions when checking Done. Users must always check
|
|
// Done before looking at the result code.
|
|
completed_ = true;
|
|
}
|
|
|
|
} // namespace http
|