simplify etag caching requests
This commit is contained in:
@@ -10,6 +10,16 @@
|
|||||||
|
|
||||||
namespace sphaira::curl {
|
namespace sphaira::curl {
|
||||||
|
|
||||||
|
enum {
|
||||||
|
Flag_None = 0,
|
||||||
|
// requests to download send etag in the header.
|
||||||
|
// the received etag is then saved on success.
|
||||||
|
// this api is only available on downloading to file.
|
||||||
|
Flag_CacheEtag = 1 << 0,
|
||||||
|
Flag_CacheLmt = 1 << 1,
|
||||||
|
Flag_Cache = Flag_CacheEtag | Flag_CacheLmt,
|
||||||
|
};
|
||||||
|
|
||||||
enum class Priority {
|
enum class Priority {
|
||||||
Normal, // gets pushed to the back of the queue
|
Normal, // gets pushed to the back of the queue
|
||||||
High, // gets pushed to the front of the queue
|
High, // gets pushed to the front of the queue
|
||||||
@@ -46,6 +56,12 @@ struct Header {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Flags {
|
||||||
|
Flags() = default;
|
||||||
|
Flags(u32 flags) : m_flags{flags} {}
|
||||||
|
u32 m_flags{Flag_None};
|
||||||
|
};
|
||||||
|
|
||||||
struct ApiResult {
|
struct ApiResult {
|
||||||
bool success;
|
bool success;
|
||||||
long code;
|
long code;
|
||||||
@@ -99,6 +115,7 @@ struct Api {
|
|||||||
template <typename... Ts>
|
template <typename... Ts>
|
||||||
auto ToMemory(Ts&&... ts) {
|
auto ToMemory(Ts&&... ts) {
|
||||||
static_assert(std::disjunction_v<std::is_same<Url, Ts>...>, "Url must be specified");
|
static_assert(std::disjunction_v<std::is_same<Url, Ts>...>, "Url must be specified");
|
||||||
|
static_assert(!std::disjunction_v<std::is_same<Path, Ts>...>, "Path must not valid for memory");
|
||||||
Api::set_option(std::forward<Ts>(ts)...);
|
Api::set_option(std::forward<Ts>(ts)...);
|
||||||
return curl::ToMemory(*this);
|
return curl::ToMemory(*this);
|
||||||
}
|
}
|
||||||
@@ -115,6 +132,7 @@ struct Api {
|
|||||||
auto ToMemoryAsync(Ts&&... ts) {
|
auto ToMemoryAsync(Ts&&... ts) {
|
||||||
static_assert(std::disjunction_v<std::is_same<Url, Ts>...>, "Url must be specified");
|
static_assert(std::disjunction_v<std::is_same<Url, Ts>...>, "Url must be specified");
|
||||||
static_assert(std::disjunction_v<std::is_same<OnComplete, Ts>...>, "OnComplete must be specified");
|
static_assert(std::disjunction_v<std::is_same<OnComplete, Ts>...>, "OnComplete must be specified");
|
||||||
|
static_assert(!std::disjunction_v<std::is_same<Path, Ts>...>, "Path must not valid for memory");
|
||||||
Api::set_option(std::forward<Ts>(ts)...);
|
Api::set_option(std::forward<Ts>(ts)...);
|
||||||
return curl::ToMemoryAsync(*this);
|
return curl::ToMemoryAsync(*this);
|
||||||
}
|
}
|
||||||
@@ -131,6 +149,7 @@ struct Api {
|
|||||||
Url m_url;
|
Url m_url;
|
||||||
Fields m_fields{};
|
Fields m_fields{};
|
||||||
Header m_header{};
|
Header m_header{};
|
||||||
|
Flags m_flags{};
|
||||||
Path m_path{};
|
Path m_path{};
|
||||||
OnComplete m_on_complete = nullptr;
|
OnComplete m_on_complete = nullptr;
|
||||||
OnProgress m_on_progress = nullptr;
|
OnProgress m_on_progress = nullptr;
|
||||||
@@ -146,6 +165,9 @@ private:
|
|||||||
void SetOption(Header&& v) {
|
void SetOption(Header&& v) {
|
||||||
m_header = v;
|
m_header = v;
|
||||||
}
|
}
|
||||||
|
void SetOption(Flags&& v) {
|
||||||
|
m_flags = v;
|
||||||
|
}
|
||||||
void SetOption(Path&& v) {
|
void SetOption(Path&& v) {
|
||||||
m_path = v;
|
m_path = v;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -389,6 +389,7 @@ auto DownloadInternal(CURL* curl, const Api& e) -> ApiResult {
|
|||||||
const bool has_post = !e.m_fields.m_str.empty() && e.m_fields.m_str != "";
|
const bool has_post = !e.m_fields.m_str.empty() && e.m_fields.m_str != "";
|
||||||
|
|
||||||
DataStruct chunk;
|
DataStruct chunk;
|
||||||
|
Header header_in = e.m_header;
|
||||||
Header header_out;
|
Header header_out;
|
||||||
fs::FsNativeSd fs;
|
fs::FsNativeSd fs;
|
||||||
|
|
||||||
@@ -405,6 +406,13 @@ auto DownloadInternal(CURL* curl, const Api& e) -> ApiResult {
|
|||||||
log_write("failed to open file: %s\n", tmp_buf);
|
log_write("failed to open file: %s\n", tmp_buf);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (e.m_flags.m_flags & Flag_CacheEtag) {
|
||||||
|
header_in.m_map.emplace("if-none-match", cache::etag_get(e.m_path));
|
||||||
|
}
|
||||||
|
if (e.m_flags.m_flags & Flag_CacheLmt) {
|
||||||
|
header_in.m_map.emplace("if-modified-since", cache::lmt_get(e.m_path));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// reserve the first chunk
|
// reserve the first chunk
|
||||||
@@ -430,7 +438,7 @@ auto DownloadInternal(CURL* curl, const Api& e) -> ApiResult {
|
|||||||
struct curl_slist* list = NULL;
|
struct curl_slist* list = NULL;
|
||||||
ON_SCOPE_EXIT(if (list) { curl_slist_free_all(list); } );
|
ON_SCOPE_EXIT(if (list) { curl_slist_free_all(list); } );
|
||||||
|
|
||||||
for (auto& [key, value] : e.m_header.m_map) {
|
for (const auto& [key, value] : header_in.m_map) {
|
||||||
if (value.empty()) {
|
if (value.empty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -481,6 +489,13 @@ auto DownloadInternal(CURL* curl, const Api& e) -> ApiResult {
|
|||||||
fsFileClose(&chunk.f);
|
fsFileClose(&chunk.f);
|
||||||
|
|
||||||
if (res == CURLE_OK && http_code != 304) {
|
if (res == CURLE_OK && http_code != 304) {
|
||||||
|
if (e.m_flags.m_flags & Flag_CacheEtag) {
|
||||||
|
cache::etag_set(e.m_path, header_out);
|
||||||
|
}
|
||||||
|
if (e.m_flags.m_flags & Flag_CacheLmt) {
|
||||||
|
cache::lmt_set(e.m_path, header_out);
|
||||||
|
}
|
||||||
|
|
||||||
fs.DeleteFile(e.m_path, true);
|
fs.DeleteFile(e.m_path, true);
|
||||||
fs.CreateDirectoryRecursivelyWithPath(e.m_path, true);
|
fs.CreateDirectoryRecursivelyWithPath(e.m_path, true);
|
||||||
if (R_FAILED(fs.RenameFile(tmp_buf, e.m_path, true))) {
|
if (R_FAILED(fs.RenameFile(tmp_buf, e.m_path, true))) {
|
||||||
|
|||||||
@@ -716,15 +716,9 @@ EntryMenu::EntryMenu(Entry& entry, const LazyImage& default_icon, Menu& menu)
|
|||||||
curl::Api().ToFileAsync(
|
curl::Api().ToFileAsync(
|
||||||
curl::Url{url},
|
curl::Url{url},
|
||||||
curl::Path{path},
|
curl::Path{path},
|
||||||
curl::Header{
|
curl::Flags{curl::Flag_Cache},
|
||||||
{ "if-none-match", curl::cache::etag_get(path) },
|
|
||||||
{ "if-modified-since", curl::cache::lmt_get(path) },
|
|
||||||
},
|
|
||||||
curl::OnComplete{[this, path](auto& result){
|
curl::OnComplete{[this, path](auto& result){
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
curl::cache::etag_set(result.path, result.header);
|
|
||||||
curl::cache::lmt_set(result.path, result.header);
|
|
||||||
|
|
||||||
if (result.code == 304) {
|
if (result.code == 304) {
|
||||||
m_banner.cached = false;
|
m_banner.cached = false;
|
||||||
} else {
|
} else {
|
||||||
@@ -1030,15 +1024,9 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"AppStore"_i18n}
|
|||||||
curl::Api().ToFileAsync(
|
curl::Api().ToFileAsync(
|
||||||
curl::Url{URL_JSON},
|
curl::Url{URL_JSON},
|
||||||
curl::Path{REPO_PATH},
|
curl::Path{REPO_PATH},
|
||||||
curl::Header{
|
curl::Flags{curl::Flag_Cache},
|
||||||
{ "if-none-match", curl::cache::etag_get(REPO_PATH) },
|
|
||||||
{ "if-modified-since", curl::cache::lmt_get(REPO_PATH) },
|
|
||||||
},
|
|
||||||
curl::OnComplete{[this](auto& result){
|
curl::OnComplete{[this](auto& result){
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
curl::cache::etag_set(result.path, result.header);
|
|
||||||
curl::cache::lmt_set(result.path, result.header);
|
|
||||||
|
|
||||||
if (result.code == 304) {
|
if (result.code == 304) {
|
||||||
log_write("appstore json not updated\n");
|
log_write("appstore json not updated\n");
|
||||||
} else {
|
} else {
|
||||||
@@ -1126,16 +1114,10 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
|||||||
curl::Api().ToFileAsync(
|
curl::Api().ToFileAsync(
|
||||||
curl::Url{url},
|
curl::Url{url},
|
||||||
curl::Path{path},
|
curl::Path{path},
|
||||||
curl::Header{
|
curl::Flags{curl::Flag_Cache},
|
||||||
{ "if-none-match", curl::cache::etag_get(path) },
|
|
||||||
{ "if-modified-since", curl::cache::lmt_get(path) },
|
|
||||||
},
|
|
||||||
curl::OnComplete{[this, &image](auto& result) {
|
curl::OnComplete{[this, &image](auto& result) {
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
curl::cache::etag_set(result.path, result.header);
|
image.state = ImageDownloadState::Done;
|
||||||
curl::cache::lmt_set(result.path, result.header);
|
|
||||||
|
|
||||||
image.state = ImageDownloadState::Done;
|
|
||||||
// data hasn't changed
|
// data hasn't changed
|
||||||
if (result.code == 304) {
|
if (result.code == 304) {
|
||||||
log_write("downloaded appstore image, was cached\n");
|
log_write("downloaded appstore image, was cached\n");
|
||||||
|
|||||||
@@ -210,10 +210,9 @@ auto DownloadAssetJson(ProgressBox* pbox, const std::string& url, GhApiEntry& ou
|
|||||||
curl::Url{url},
|
curl::Url{url},
|
||||||
curl::Path{path},
|
curl::Path{path},
|
||||||
curl::OnProgress{pbox->OnDownloadProgressCallback()},
|
curl::OnProgress{pbox->OnDownloadProgressCallback()},
|
||||||
|
curl::Flags{curl::Flag_Cache},
|
||||||
curl::Header{
|
curl::Header{
|
||||||
{ "Accept", "application/vnd.github+json" },
|
{ "Accept", "application/vnd.github+json" },
|
||||||
{ "if-none-match", curl::cache::etag_get(path) },
|
|
||||||
{ "if-modified-since", curl::cache::lmt_get(path) },
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -222,8 +221,6 @@ auto DownloadAssetJson(ProgressBox* pbox, const std::string& url, GhApiEntry& ou
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
curl::cache::etag_set(result.path, result.header);
|
|
||||||
curl::cache::lmt_set(result.path, result.header);
|
|
||||||
from_json(result.path, out);
|
from_json(result.path, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -145,10 +145,9 @@ MainMenu::MainMenu() {
|
|||||||
curl::Api().ToFileAsync(
|
curl::Api().ToFileAsync(
|
||||||
curl::Url{GITHUB_URL},
|
curl::Url{GITHUB_URL},
|
||||||
curl::Path{CACHE_PATH},
|
curl::Path{CACHE_PATH},
|
||||||
|
curl::Flags{curl::Flag_Cache},
|
||||||
curl::Header{
|
curl::Header{
|
||||||
{ "Accept", "application/vnd.github+json" },
|
{ "Accept", "application/vnd.github+json" },
|
||||||
{ "if-none-match", curl::cache::etag_get(CACHE_PATH) },
|
|
||||||
{ "if-modified-since", curl::cache::lmt_get(CACHE_PATH) },
|
|
||||||
},
|
},
|
||||||
curl::OnComplete{[this](auto& result){
|
curl::OnComplete{[this](auto& result){
|
||||||
log_write("inside github download\n");
|
log_write("inside github download\n");
|
||||||
@@ -165,9 +164,6 @@ MainMenu::MainMenu() {
|
|||||||
log_write("etag changed\n");
|
log_write("etag changed\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
curl::cache::etag_set(result.path, result.header);
|
|
||||||
curl::cache::lmt_set(result.path, result.header);
|
|
||||||
|
|
||||||
auto json = yyjson_read_file(CACHE_PATH, YYJSON_READ_NOFLAG, nullptr, nullptr);
|
auto json = yyjson_read_file(CACHE_PATH, YYJSON_READ_NOFLAG, nullptr, nullptr);
|
||||||
R_UNLESS(json, false);
|
R_UNLESS(json, false);
|
||||||
ON_SCOPE_EXIT(yyjson_doc_free(json));
|
ON_SCOPE_EXIT(yyjson_doc_free(json));
|
||||||
|
|||||||
@@ -656,15 +656,9 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
|||||||
curl::Api().ToFileAsync(
|
curl::Api().ToFileAsync(
|
||||||
curl::Url{url},
|
curl::Url{url},
|
||||||
curl::Path{path},
|
curl::Path{path},
|
||||||
curl::Header{
|
curl::Flags{curl::Flag_Cache},
|
||||||
{ "if-none-match", curl::cache::etag_get(path) },
|
|
||||||
{ "if-modified-since", curl::cache::lmt_get(path) },
|
|
||||||
},
|
|
||||||
curl::OnComplete{[this, &image](auto& result) {
|
curl::OnComplete{[this, &image](auto& result) {
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
curl::cache::etag_set(result.path, result.header);
|
|
||||||
curl::cache::lmt_set(result.path, result.header);
|
|
||||||
|
|
||||||
image.state = ImageDownloadState::Done;
|
image.state = ImageDownloadState::Done;
|
||||||
// data hasn't changed
|
// data hasn't changed
|
||||||
if (result.code == 304) {
|
if (result.code == 304) {
|
||||||
@@ -751,10 +745,7 @@ void Menu::PackListDownload() {
|
|||||||
curl::Api().ToFileAsync(
|
curl::Api().ToFileAsync(
|
||||||
curl::Url{packList_url},
|
curl::Url{packList_url},
|
||||||
curl::Path{packlist_path},
|
curl::Path{packlist_path},
|
||||||
curl::Header{
|
curl::Flags{curl::Flag_Cache},
|
||||||
{ "if-none-match", curl::cache::etag_get(packlist_path) },
|
|
||||||
{ "if-modified-since", curl::cache::lmt_get(packlist_path) },
|
|
||||||
},
|
|
||||||
curl::OnComplete{[this, page_index](auto& result){
|
curl::OnComplete{[this, page_index](auto& result){
|
||||||
log_write("got themezer data\n");
|
log_write("got themezer data\n");
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
@@ -764,9 +755,6 @@ void Menu::PackListDownload() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
curl::cache::etag_set(result.path, result.header);
|
|
||||||
curl::cache::lmt_set(result.path, result.header);
|
|
||||||
|
|
||||||
PackList a;
|
PackList a;
|
||||||
from_json(result.path, a);
|
from_json(result.path, a);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user