pairinteraction
A Rydberg Interaction Calculator
GitHubDownloader.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2025 Pairinteraction Developers
2// SPDX-License-Identifier: LGPL-3.0-or-later
3
5
7
8#include <filesystem>
9#include <fmt/core.h>
10#include <fstream>
11#include <future>
12#include <httplib.h>
13#include <stdexcept>
14
15namespace pairinteraction {
16
17GitHubDownloader::GitHubDownloader() : client(std::make_unique<httplib::SSLClient>(host)) {
19 if (!std::filesystem::exists(configdir)) {
20 std::filesystem::create_directories(configdir);
21 } else if (!std::filesystem::is_directory(configdir)) {
22 throw std::filesystem::filesystem_error("Cannot access config directory ",
23 configdir.string(),
24 std::make_error_code(std::errc::not_a_directory));
25 }
26
27 std::filesystem::path cert_path = configdir / "ca-bundle.crt";
28 if (!std::filesystem::exists(cert_path)) {
29 std::ofstream out(cert_path);
30 if (!out) {
31 throw std::runtime_error("Failed to create certificate file at " + cert_path.string());
32 }
33 out << cert;
34 out.close();
35 }
36
37 client->set_follow_location(true);
38 client->set_connection_timeout(5, 0); // seconds
39 client->set_read_timeout(60, 0); // seconds
40 client->set_write_timeout(1, 0); // seconds
41 client->set_ca_cert_path(cert_path.string());
42}
43
45
46std::future<GitHubDownloader::Result>
47GitHubDownloader::download(const std::string &remote_url, const std::string &if_modified_since,
48 bool use_octet_stream) const {
49 return std::async(
50 std::launch::async, [this, remote_url, if_modified_since, use_octet_stream]() -> Result {
51 // Prepare headers
52 httplib::Headers headers{
53 {"X-GitHub-Api-Version", "2022-11-28"},
54 {"Accept",
55 use_octet_stream ? "application/octet-stream" : "application/vnd.github+json"}};
56
57 if (!if_modified_since.empty()) {
58 headers.emplace("if-modified-since", if_modified_since);
59 }
60
61 // Use the GitHub token if available; otherwise, if we have a conditional request,
62 // insert a dummy authorization header to avoid increasing rate limits
63 if (auto *token = std::getenv("GITHUB_TOKEN"); token) {
64 headers.emplace("Authorization", fmt::format("Bearer {}", token));
65 } else if (!if_modified_since.empty()) {
66 headers.emplace("Authorization",
67 "avoids-an-increase-in-ratelimits-used-if-304-is-returned");
68 }
69
70 auto response = client->Get(remote_url, headers);
71
72 // Handle if the response is null
73 if (!response) {
74 // Defensive handling: if response is null and the error is unknown,
75 // treat this as a 304 Not Modified
76 if (response.error() == httplib::Error::Unknown) {
77 return Result{304, "", "", {}};
78 }
79 throw std::runtime_error(fmt::format("Error downloading '{}': {}", remote_url,
80 httplib::to_string(response.error())));
81 }
82
83 // Parse the response
84 Result result;
85 if (response->has_header("x-ratelimit-remaining")) {
86 result.rate_limit.remaining =
87 std::stoi(response->get_header_value("x-ratelimit-remaining"));
88 }
89 if (response->has_header("x-ratelimit-reset")) {
90 result.rate_limit.reset_time =
91 std::stoi(response->get_header_value("x-ratelimit-reset"));
92 }
93 if (response->has_header("last-modified")) {
94 result.last_modified = response->get_header_value("last-modified");
95 }
96 result.body = response->body;
97 result.status_code = response->status;
98 return result;
99 });
100}
101
102GitHubDownloader::RateLimit GitHubDownloader::get_rate_limit() const {
103 // This call now either returns valid rate limit data or throws an exception on error
104 Result result = download("/rate_limit", "", false).get();
105 if (result.status_code != 200) {
106 throw std::runtime_error(
107 fmt::format("Failed obtaining the rate limit: status code {}.", result.status_code));
108 }
109 return result.rate_limit;
110}
111
112std::string GitHubDownloader::get_host() const { return "https://" + host; }
113
114} // namespace pairinteraction
virtual std::future< Result > download(const std::string &remote_url, const std::string &if_modified_since="", bool use_octet_stream=false) const
std::filesystem::path get_config_directory()
Definition: paths.hpp:61