mirror of
https://github.com/geode-sdk/geode.git
synced 2025-03-31 07:10:28 -04:00
implement the last modified since & responseHeaders in async web
This commit is contained in:
parent
065d0c4926
commit
df07409532
4 changed files with 111 additions and 50 deletions
loader
|
@ -60,6 +60,7 @@ namespace geode::utils::web {
|
|||
using AsyncExpectCode = utils::MiniFunction<void(std::string const&, int)>;
|
||||
using AsyncThen = utils::MiniFunction<void(SentAsyncWebRequest&, ByteVector const&)>;
|
||||
using AsyncCancelled = utils::MiniFunction<void(SentAsyncWebRequest&)>;
|
||||
using AsyncResponseHeader = utils::MiniFunction<void(std::unordered_map<std::string, std::string> const&)>;
|
||||
|
||||
/**
|
||||
* A handle to an in-progress sent asynchronous web request. Use this to
|
||||
|
@ -97,6 +98,11 @@ namespace geode::utils::web {
|
|||
* Check if the request is finished
|
||||
*/
|
||||
bool finished() const;
|
||||
|
||||
/**
|
||||
* Get the response header of the request. Only valid after the request
|
||||
*/
|
||||
std::string getResponseHeader(std::string_view header) const;
|
||||
};
|
||||
|
||||
using SentAsyncWebRequestHandle = std::shared_ptr<SentAsyncWebRequest>;
|
||||
|
|
|
@ -44,24 +44,65 @@ bool s_isNewUpdateDownloaded = false;
|
|||
|
||||
void updater::fetchLatestGithubRelease(
|
||||
const utils::MiniFunction<void(matjson::Value const&)>& then,
|
||||
utils::MiniFunction<void(std::string const&)> expect
|
||||
utils::MiniFunction<void(std::string const&)> expect, bool force
|
||||
) {
|
||||
if (s_latestGithubRelease) {
|
||||
return then(s_latestGithubRelease.value());
|
||||
}
|
||||
|
||||
std::string modifiedSince;
|
||||
if (!force) {
|
||||
modifiedSince = Mod::get()->getSavedValue("last-modified-github-release-check", std::string());
|
||||
}
|
||||
|
||||
// TODO: add header to not get rate limited
|
||||
web::AsyncWebRequest()
|
||||
.join("loader-auto-update-check")
|
||||
.header("If-Modified-Since", modifiedSince)
|
||||
.userAgent("github_api/1.0")
|
||||
.fetch("https://api.github.com/repos/geode-sdk/geode/releases/latest")
|
||||
.json()
|
||||
.then([then](matjson::Value const& json) {
|
||||
.then([then](web::SentAsyncWebRequest& req, matjson::Value const& json) {
|
||||
Mod::get()->setSavedValue("last-modified-auto-update-check", req.getResponseHeader("Last-Modified"));
|
||||
s_latestGithubRelease = json;
|
||||
then(json);
|
||||
})
|
||||
.expect(std::move(expect));
|
||||
}
|
||||
|
||||
void updater::downloadLatestLoaderResources() {
|
||||
log::debug("Downloading latest resources", Loader::get()->getVersion().toString());
|
||||
fetchLatestGithubRelease(
|
||||
[](matjson::Value const& raw) {
|
||||
auto json = raw;
|
||||
JsonChecker checker(json);
|
||||
auto root = checker.root("[]").obj();
|
||||
|
||||
// find release asset
|
||||
for (auto asset : root.needs("assets").iterate()) {
|
||||
auto obj = asset.obj();
|
||||
if (obj.needs("name").template get<std::string>() == "resources.zip") {
|
||||
updater::tryDownloadLoaderResources(
|
||||
obj.needs("browser_download_url").template get<std::string>(),
|
||||
false
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ResourceDownloadEvent(
|
||||
UpdateFailed("Unable to find resources in latest GitHub release")
|
||||
).post();
|
||||
},
|
||||
[](std::string const& info) {
|
||||
ResourceDownloadEvent(
|
||||
UpdateFailed("Unable to download resources: " + info)
|
||||
).post();
|
||||
},
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
void updater::tryDownloadLoaderResources(
|
||||
std::string const& url,
|
||||
bool tryLatestOnError
|
||||
|
@ -118,62 +159,41 @@ void updater::updateSpecialFiles() {
|
|||
void updater::downloadLoaderResources(bool useLatestRelease) {
|
||||
web::AsyncWebRequest()
|
||||
.join("loader-tag-exists-check")
|
||||
.header("If-Modified-Since", Mod::get()->getSavedValue("last-modified-tag-exists-check", std::string()))
|
||||
.userAgent("github_api/1.0")
|
||||
.fetch(fmt::format(
|
||||
"https://api.github.com/repos/geode-sdk/geode/git/ref/tags/{}",
|
||||
Loader::get()->getVersion().toString()
|
||||
))
|
||||
.fetch("https://api.github.com/repos/geode-sdk/geode/releases/tags/" + Loader::get()->getVersion().toString())
|
||||
.json()
|
||||
.then([](matjson::Value const& json) {
|
||||
updater::tryDownloadLoaderResources(fmt::format(
|
||||
"https://github.com/geode-sdk/geode/releases/download/{}/resources.zip",
|
||||
Loader::get()->getVersion().toString()
|
||||
), true);
|
||||
})
|
||||
.expect([=](std::string const& info, int code) {
|
||||
if (code == 404) {
|
||||
if (useLatestRelease) {
|
||||
log::debug("Loader version {} does not exist on Github, downloading latest resources", Loader::get()->getVersion().toString());
|
||||
fetchLatestGithubRelease(
|
||||
[](matjson::Value const& raw) {
|
||||
auto json = raw;
|
||||
JsonChecker checker(json);
|
||||
auto root = checker.root("[]").obj();
|
||||
.then([](web::SentAsyncWebRequest& req, matjson::Value const& json) {
|
||||
Mod::get()->setSavedValue("last-modified-tag-exists-check", req.getResponseHeader("Last-Modified"));
|
||||
auto raw = json;
|
||||
JsonChecker checker(raw);
|
||||
auto root = checker.root("[]").obj();
|
||||
|
||||
// find release asset
|
||||
for (auto asset : root.needs("assets").iterate()) {
|
||||
auto obj = asset.obj();
|
||||
if (obj.needs("name").template get<std::string>() == "resources.zip") {
|
||||
updater::tryDownloadLoaderResources(
|
||||
obj.needs("browser_download_url").template get<std::string>(),
|
||||
false
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ResourceDownloadEvent(
|
||||
UpdateFailed("Unable to find resources in latest GitHub release")
|
||||
).post();
|
||||
},
|
||||
[](std::string const& info) {
|
||||
ResourceDownloadEvent(
|
||||
UpdateFailed("Unable to download resources: " + info)
|
||||
).post();
|
||||
}
|
||||
// find release asset
|
||||
for (auto asset : root.needs("assets").iterate()) {
|
||||
auto obj = asset.obj();
|
||||
if (obj.needs("name").template get<std::string>() == "resources.zip") {
|
||||
updater::tryDownloadLoaderResources(
|
||||
obj.needs("browser_download_url").template get<std::string>(),
|
||||
false
|
||||
);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
log::debug("Loader version {} does not exist on GitHub, not downloading the resources", Loader::get()->getVersion().toString());
|
||||
}
|
||||
ResourceDownloadEvent(
|
||||
UpdateFinished()
|
||||
).post();
|
||||
}
|
||||
|
||||
ResourceDownloadEvent(
|
||||
UpdateFailed("Unable to find resources in release")
|
||||
).post();
|
||||
})
|
||||
.expect([=](std::string const& info, int code) {
|
||||
if (useLatestRelease) {
|
||||
log::debug("Loader version {} does not exist, trying to download latest resources");
|
||||
downloadLatestLoaderResources();
|
||||
}
|
||||
else {
|
||||
log::debug("Loader version {} does not exist on GitHub, not downloading the resources", Loader::get()->getVersion().toString());
|
||||
ResourceDownloadEvent(
|
||||
UpdateFailed("Unable to check if tag exists: " + info)
|
||||
UpdateFinished()
|
||||
).post();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -34,10 +34,12 @@ namespace geode::updater {
|
|||
void updateSpecialFiles();
|
||||
void tryDownloadLoaderResources(std::string const& url, bool tryLatestOnError = true);
|
||||
void downloadLoaderResources(bool useLatestRelease = false);
|
||||
void downloadLatestLoaderResources();
|
||||
void downloadLoaderUpdate(std::string const& url);
|
||||
void fetchLatestGithubRelease(
|
||||
const utils::MiniFunction<void(matjson::Value const&)>& then,
|
||||
utils::MiniFunction<void(std::string const&)> expect
|
||||
utils::MiniFunction<void(std::string const&)> expect,
|
||||
bool force = false
|
||||
);
|
||||
|
||||
bool verifyLoaderResources();
|
||||
|
|
|
@ -151,6 +151,7 @@ private:
|
|||
std::vector<AsyncExpectCode> m_expects;
|
||||
std::vector<AsyncProgress> m_progresses;
|
||||
std::vector<AsyncCancelled> m_cancelleds;
|
||||
std::unordered_map<std::string, std::string> m_responseHeader;
|
||||
Status m_status = Status::Paused;
|
||||
std::atomic<bool> m_paused = true;
|
||||
std::atomic<bool> m_cancelled = false;
|
||||
|
@ -185,6 +186,12 @@ public:
|
|||
void cancel();
|
||||
bool finished() const;
|
||||
|
||||
std::string getResponseHeader(std::string_view header) const {
|
||||
auto it = m_responseHeader.find(std::string(header));
|
||||
if (it == m_responseHeader.end()) return "";
|
||||
return it->second;
|
||||
}
|
||||
|
||||
friend class SentAsyncWebRequest;
|
||||
};
|
||||
|
||||
|
@ -324,6 +331,28 @@ SentAsyncWebRequest::Impl::Impl(SentAsyncWebRequest* self, AsyncWebRequest const
|
|||
std::ofstream* file;
|
||||
} data{this, file.get()};
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &data);
|
||||
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, +[](char* buffer, size_t size, size_t nitems, void* ptr){
|
||||
auto data = static_cast<ProgressData*>(ptr);
|
||||
std::string header;
|
||||
header.append(buffer, size * nitems);
|
||||
// send the header to the response header callback
|
||||
Loader::get()->queueInMainThread([self = data->self, header]() {
|
||||
std::unordered_map<std::string, std::string> headers;
|
||||
std::string line;
|
||||
std::stringstream ss(header);
|
||||
while (std::getline(ss, line)) {
|
||||
auto colon = line.find(':');
|
||||
if (colon == std::string::npos) continue;
|
||||
auto key = line.substr(0, colon);
|
||||
auto value = line.substr(colon + 1);
|
||||
headers[key] = value;
|
||||
}
|
||||
self->m_responseHeader = std::move(headers);
|
||||
});
|
||||
return size * nitems;
|
||||
});
|
||||
|
||||
curl_easy_setopt(
|
||||
curl,
|
||||
CURLOPT_PROGRESSFUNCTION,
|
||||
|
@ -461,6 +490,10 @@ std::shared_ptr<SentAsyncWebRequest> SentAsyncWebRequest::create(AsyncWebRequest
|
|||
ret->m_impl = std::move(std::make_shared<SentAsyncWebRequest::Impl>(ret.get(), request, id));
|
||||
return ret;
|
||||
}
|
||||
std::string SentAsyncWebRequest::getResponseHeader(std::string_view header) const {
|
||||
return m_impl->getResponseHeader(header);
|
||||
}
|
||||
|
||||
void SentAsyncWebRequest::doCancel() {
|
||||
return m_impl->doCancel();
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue