mirror of
https://github.com/geode-sdk/geode.git
synced 2025-03-28 13:52:07 -04:00
Add post & custom requests and user agents to AsyncWebRequest
This commit is contained in:
parent
7816c435c4
commit
c256207457
5 changed files with 119 additions and 21 deletions
|
@ -8,6 +8,7 @@
|
|||
* Implement UI for selection of downloading specific mod versions (5d15eb0)
|
||||
* Change install & uninstall popups to reflect the new changes (d40f467)
|
||||
* Keep the scroll when enabling, installing etc. a mod (b3d444a)
|
||||
* Update MacOS crashlog to include base and offset (7816c43)
|
||||
|
||||
## v1.2.1
|
||||
* Mods now target macOS 10.13 instead of 10.14 (7cc1cd4)
|
||||
|
|
|
@ -104,6 +104,16 @@ namespace geode::utils::web {
|
|||
template <class T>
|
||||
using DataConverter = Result<T> (*)(ByteVector const&);
|
||||
|
||||
// Hack until 2.0.0 to store extra members in AsyncWebRequest
|
||||
struct AsyncWebRequestData {
|
||||
std::variant<std::monostate, std::ostream*, ghc::filesystem::path> m_target;
|
||||
std::string m_userAgent;
|
||||
std::string m_customRequest;
|
||||
bool m_isPostRequest = false;
|
||||
std::string m_postFields;
|
||||
bool m_isJsonRequest = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* An asynchronous, thread-safe web request. Downloads data from the
|
||||
* internet without slowing the main thread. All callbacks are run in the
|
||||
|
@ -111,6 +121,9 @@ namespace geode::utils::web {
|
|||
*/
|
||||
class GEODE_DLL AsyncWebRequest {
|
||||
private:
|
||||
// i want to cry whose idea was to not make this pimpl
|
||||
// For 2.0.0: make this pimpl
|
||||
|
||||
std::optional<std::string> m_joinID;
|
||||
std::string m_url;
|
||||
AsyncThen m_then = nullptr;
|
||||
|
@ -118,9 +131,12 @@ namespace geode::utils::web {
|
|||
AsyncProgress m_progress = nullptr;
|
||||
AsyncCancelled m_cancelled = nullptr;
|
||||
bool m_sent = false;
|
||||
std::variant<std::monostate, std::ostream*, ghc::filesystem::path> m_target;
|
||||
mutable std::variant<std::monostate, AsyncWebRequestData> m_extra;
|
||||
std::vector<std::string> m_httpHeaders;
|
||||
|
||||
AsyncWebRequestData& extra();
|
||||
AsyncWebRequestData const& extra() const;
|
||||
|
||||
template <class T>
|
||||
friend class AsyncWebResult;
|
||||
friend class SentAsyncWebRequest;
|
||||
|
@ -151,6 +167,29 @@ namespace geode::utils::web {
|
|||
* Can be called more than once.
|
||||
*/
|
||||
AsyncWebRequest& header(std::string const& header);
|
||||
/**
|
||||
* In order to specify an user agent to the request, give it here.
|
||||
*/
|
||||
AsyncWebRequest& userAgent(std::string const& userAgent);
|
||||
/**
|
||||
* Specify that the request is a POST request.
|
||||
*/
|
||||
AsyncWebRequest& postRequest();
|
||||
/**
|
||||
* Specify that the request is a custom request like PUT and DELETE.
|
||||
*/
|
||||
AsyncWebRequest& customRequest(std::string const& request);
|
||||
/**
|
||||
* Specify the post fields to send with the request. Only valid if
|
||||
* `postRequest` or `customRequest` was called before.
|
||||
*/
|
||||
AsyncWebRequest& postFields(std::string const& fields);
|
||||
/**
|
||||
* Specify the post fields to send with the request. Only valid if
|
||||
* `postRequest` or `customRequest` was called before. Additionally
|
||||
* sets the content type to application/json.
|
||||
*/
|
||||
AsyncWebRequest& postFields(json::Value const& fields);
|
||||
/**
|
||||
* URL to fetch from the internet asynchronously
|
||||
* @param url URL of the data to download. Redirects will be
|
||||
|
|
|
@ -361,6 +361,7 @@ void Index::Impl::checkForUpdates() {
|
|||
auto oldSHA = file::readString(checksum).unwrapOr("");
|
||||
web::AsyncWebRequest()
|
||||
.join("index-update")
|
||||
.userAgent("github_api/1.0")
|
||||
.header(fmt::format("If-None-Match: \"{}\"", oldSHA))
|
||||
.header("Accept: application/vnd.github.sha")
|
||||
.fetch("https://api.github.com/repos/geode-sdk/mods/commits/main")
|
||||
|
|
|
@ -700,6 +700,7 @@ void Loader::Impl::fetchLatestGithubRelease(
|
|||
// TODO: add header to not get rate limited
|
||||
web::AsyncWebRequest()
|
||||
.join("loader-auto-update-check")
|
||||
.userAgent("github_api/1.0")
|
||||
.fetch("https://api.github.com/repos/geode-sdk/geode/releases/latest")
|
||||
.json()
|
||||
.then([this, then](json::Value const& json) {
|
||||
|
@ -768,6 +769,7 @@ void Loader::Impl::downloadLoaderResources(bool useLatestRelease) {
|
|||
if (!useLatestRelease) {
|
||||
web::AsyncWebRequest()
|
||||
.join("loader-tag-exists-check")
|
||||
.userAgent("github_api/1.0")
|
||||
.fetch(fmt::format(
|
||||
"https://api.github.com/repos/geode-sdk/geode/git/ref/tags/{}",
|
||||
this->getVersion().toString()
|
||||
|
|
|
@ -48,7 +48,6 @@ Result<> web::fetchFile(
|
|||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &file);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, utils::fetch::writeBinaryData);
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, "github_api/1.0");
|
||||
if (prog) {
|
||||
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
|
||||
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, utils::fetch::progress);
|
||||
|
@ -81,7 +80,6 @@ Result<ByteVector> web::fetchBytes(std::string const& url) {
|
|||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ret);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, utils::fetch::writeBytes);
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, "github_api/1.0");
|
||||
auto res = curl_easy_perform(curl);
|
||||
if (res != CURLE_OK) {
|
||||
curl_easy_cleanup(curl);
|
||||
|
@ -120,7 +118,6 @@ Result<std::string> web::fetch(std::string const& url) {
|
|||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ret);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, utils::fetch::writeString);
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, "github_api/1.0");
|
||||
auto res = curl_easy_perform(curl);
|
||||
if (res != CURLE_OK) {
|
||||
curl_easy_cleanup(curl);
|
||||
|
@ -162,9 +159,9 @@ private:
|
|||
SentAsyncWebRequest* m_self;
|
||||
|
||||
mutable std::mutex m_mutex;
|
||||
std::variant<std::monostate, std::ostream*, ghc::filesystem::path> m_target =
|
||||
std::monostate();
|
||||
AsyncWebRequestData m_extra;
|
||||
std::vector<std::string> m_httpHeaders;
|
||||
|
||||
|
||||
template <class T>
|
||||
friend class AsyncWebResult;
|
||||
|
@ -187,7 +184,7 @@ static std::unordered_map<std::string, SentAsyncWebRequestHandle> RUNNING_REQUES
|
|||
static std::mutex RUNNING_REQUESTS_MUTEX;
|
||||
|
||||
SentAsyncWebRequest::Impl::Impl(SentAsyncWebRequest* self, AsyncWebRequest const& req, std::string const& id) :
|
||||
m_id(id), m_url(req.m_url), m_target(req.m_target), m_httpHeaders(req.m_httpHeaders) {
|
||||
m_id(id), m_url(req.m_url), m_extra(req.extra()), m_httpHeaders(req.m_httpHeaders) {
|
||||
|
||||
#define AWAIT_RESUME() \
|
||||
{\
|
||||
|
@ -221,16 +218,16 @@ SentAsyncWebRequest::Impl::Impl(SentAsyncWebRequest* self, AsyncWebRequest const
|
|||
std::unique_ptr<std::ofstream> file = nullptr;
|
||||
|
||||
// into file
|
||||
if (std::holds_alternative<ghc::filesystem::path>(m_target)) {
|
||||
if (std::holds_alternative<ghc::filesystem::path>(m_extra.m_target)) {
|
||||
file = std::make_unique<std::ofstream>(
|
||||
std::get<ghc::filesystem::path>(m_target), std::ios::out | std::ios::binary
|
||||
std::get<ghc::filesystem::path>(m_extra.m_target), std::ios::out | std::ios::binary
|
||||
);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, file.get());
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, utils::fetch::writeBinaryData);
|
||||
}
|
||||
// into stream
|
||||
else if (std::holds_alternative<std::ostream*>(m_target)) {
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, std::get<std::ostream*>(m_target));
|
||||
else if (std::holds_alternative<std::ostream*>(m_extra.m_target)) {
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, std::get<std::ostream*>(m_extra.m_target));
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, utils::fetch::writeBinaryData);
|
||||
}
|
||||
// into memory
|
||||
|
@ -242,8 +239,30 @@ SentAsyncWebRequest::Impl::Impl(SentAsyncWebRequest* self, AsyncWebRequest const
|
|||
// No need to verify SSL, we trust our domains :-)
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
// Github User Agent
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, "github_api/1.0");
|
||||
// User Agent
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, m_extra.m_userAgent.c_str());
|
||||
|
||||
// Headers
|
||||
curl_slist* headers = nullptr;
|
||||
for (auto& header : m_httpHeaders) {
|
||||
headers = curl_slist_append(headers, header.c_str());
|
||||
}
|
||||
|
||||
// Post request
|
||||
if (m_extra.m_isPostRequest || m_extra.m_customRequest.size()) {
|
||||
if (m_extra.m_isPostRequest) {
|
||||
curl_easy_setopt(curl, CURLOPT_POST, 1L);
|
||||
}
|
||||
else {
|
||||
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, m_extra.m_customRequest.c_str());
|
||||
}
|
||||
if (m_extra.m_isJsonRequest) {
|
||||
headers = curl_slist_append(headers, "Content-Type: application/json");
|
||||
}
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, m_extra.m_postFields.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, m_extra.m_postFields.size());
|
||||
}
|
||||
|
||||
// Track progress
|
||||
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
|
||||
// Follow redirects
|
||||
|
@ -251,10 +270,7 @@ SentAsyncWebRequest::Impl::Impl(SentAsyncWebRequest* self, AsyncWebRequest const
|
|||
// Fail if response code is 4XX or 5XX
|
||||
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
|
||||
|
||||
curl_slist* headers = nullptr;
|
||||
for (auto& header : m_httpHeaders) {
|
||||
headers = curl_slist_append(headers, header.c_str());
|
||||
}
|
||||
// Headers end
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
||||
|
||||
struct ProgressData {
|
||||
|
@ -318,8 +334,8 @@ void SentAsyncWebRequest::Impl::doCancel() {
|
|||
m_cleanedUp = true;
|
||||
|
||||
// remove file if downloaded to one
|
||||
if (std::holds_alternative<ghc::filesystem::path>(m_target)) {
|
||||
auto path = std::get<ghc::filesystem::path>(m_target);
|
||||
if (std::holds_alternative<ghc::filesystem::path>(m_extra.m_target)) {
|
||||
auto path = std::get<ghc::filesystem::path>(m_extra.m_target);
|
||||
if (ghc::filesystem::exists(path)) {
|
||||
try {
|
||||
ghc::filesystem::remove(path);
|
||||
|
@ -410,11 +426,50 @@ void SentAsyncWebRequest::error(std::string const& error, int code) {
|
|||
return m_impl->error(error, code);
|
||||
}
|
||||
|
||||
AsyncWebRequestData& AsyncWebRequest::extra() {
|
||||
if (!std::holds_alternative<AsyncWebRequestData>(m_extra)) {
|
||||
m_extra = AsyncWebRequestData();
|
||||
}
|
||||
return std::get<AsyncWebRequestData>(m_extra);
|
||||
}
|
||||
|
||||
AsyncWebRequestData const& AsyncWebRequest::extra() const {
|
||||
if (!std::holds_alternative<AsyncWebRequestData>(m_extra)) {
|
||||
m_extra = AsyncWebRequestData();
|
||||
}
|
||||
return std::get<AsyncWebRequestData>(m_extra);
|
||||
}
|
||||
|
||||
AsyncWebRequest& AsyncWebRequest::join(std::string const& requestID) {
|
||||
m_joinID = requestID;
|
||||
return *this;
|
||||
}
|
||||
|
||||
AsyncWebRequest& AsyncWebRequest::userAgent(std::string const& userAgent) {
|
||||
this->extra().m_userAgent = userAgent;
|
||||
return *this;
|
||||
}
|
||||
|
||||
AsyncWebRequest& AsyncWebRequest::postRequest() {
|
||||
this->extra().m_isPostRequest = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
AsyncWebRequest& AsyncWebRequest::customRequest(std::string const& request) {
|
||||
this->extra().m_customRequest = request;
|
||||
return *this;
|
||||
}
|
||||
|
||||
AsyncWebRequest& AsyncWebRequest::postFields(std::string const& fields) {
|
||||
this->extra().m_postFields = fields;
|
||||
return *this;
|
||||
}
|
||||
|
||||
AsyncWebRequest& AsyncWebRequest::postFields(json::Value const& fields) {
|
||||
this->extra().m_isJsonRequest = true;
|
||||
return this->postFields(fields.dump());
|
||||
}
|
||||
|
||||
AsyncWebRequest& AsyncWebRequest::header(std::string const& header) {
|
||||
m_httpHeaders.push_back(header);
|
||||
return *this;
|
||||
|
@ -489,14 +544,14 @@ AsyncWebRequest::~AsyncWebRequest() {
|
|||
}
|
||||
|
||||
AsyncWebResult<std::monostate> AsyncWebResponse::into(std::ostream& stream) {
|
||||
m_request.m_target = &stream;
|
||||
m_request.extra().m_target = &stream;
|
||||
return this->as(+[](ByteVector const&) -> Result<std::monostate> {
|
||||
return Ok(std::monostate());
|
||||
});
|
||||
}
|
||||
|
||||
AsyncWebResult<std::monostate> AsyncWebResponse::into(ghc::filesystem::path const& path) {
|
||||
m_request.m_target = path;
|
||||
m_request.extra().m_target = path;
|
||||
return this->as(+[](ByteVector const&) -> Result<std::monostate> {
|
||||
return Ok(std::monostate());
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue