12 #include "../../stdafx.h"
13 #include "../../debug.h"
14 #include "../../fileio_func.h"
15 #include "../../rev.h"
16 #include "../../thread.h"
17 #include "../network_internal.h"
23 #include <condition_variable>
24 #include <curl/curl.h>
28 #include "../../safeguards.h"
32 static auto _certificate_files = {
33 "/etc/ssl/certs/ca-certificates.crt",
34 "/etc/pki/tls/certs/ca-bundle.crt",
35 "/etc/ssl/ca-bundle.pem",
36 "/etc/pki/tls/cacert.pem",
37 "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem",
41 static auto _certificate_directories = {
44 "/system/etc/security/cacerts",
48 static std::vector<HTTPThreadSafeCallback *> _http_callbacks;
49 static std::vector<HTTPThreadSafeCallback *> _new_http_callbacks;
50 static std::mutex _http_callback_mutex;
51 static std::mutex _new_http_callback_mutex;
68 std::lock_guard<std::mutex>
lock(_new_http_callback_mutex);
69 _new_http_callbacks.push_back(&this->callback);
74 std::lock_guard<std::mutex>
lock(_http_callback_mutex);
75 _http_callbacks.erase(std::remove(_http_callbacks.begin(), _http_callbacks.end(), &this->callback), _http_callbacks.end());
78 const std::string
uri;
83 static std::thread _http_thread;
84 static std::atomic<bool> _http_thread_exit =
false;
85 static std::queue<std::unique_ptr<NetworkHTTPRequest>> _http_requests;
86 static std::mutex _http_mutex;
87 static std::condition_variable _http_cv;
89 static std::string _http_ca_file =
"";
90 static std::string _http_ca_path =
"";
96 if (_http_ca_file.empty() && _http_ca_path.empty()) {
102 std::lock_guard<std::mutex>
lock(_http_mutex);
103 _http_requests.push(std::make_unique<NetworkHTTPRequest>(uri, callback, data));
104 _http_cv.notify_one();
109 std::lock_guard<std::mutex>
lock(_http_callback_mutex);
112 std::lock_guard<std::mutex> lock_new(_new_http_callback_mutex);
113 if (!_new_http_callbacks.empty()) {
115 _http_callbacks.insert(_http_callbacks.end(), _new_http_callbacks.begin(), _new_http_callbacks.end());
116 _new_http_callbacks.clear();
120 for (
auto &callback : _http_callbacks) {
121 callback->HandleQueue();
127 CURL *curl = curl_easy_init();
128 assert(curl !=
nullptr);
131 std::unique_lock<std::mutex>
lock(_http_mutex);
134 while (_http_requests.empty() && !_http_thread_exit) {
137 if (_http_thread_exit)
break;
139 std::unique_ptr<NetworkHTTPRequest> request = std::move(_http_requests.front());
140 _http_requests.pop();
146 curl_easy_reset(curl);
147 curl_slist *headers =
nullptr;
149 if (_debug_net_level >= 5) {
150 curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
154 std::string user_agent = fmt::format(
"OpenTTD/{}", GetNetworkRevisionString());
155 curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent.c_str());
156 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
157 curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 5L);
161 curl_easy_setopt(curl, CURLOPT_CAINFO, _http_ca_file.empty() ?
nullptr : _http_ca_file.c_str());
162 curl_easy_setopt(curl, CURLOPT_CAPATH, _http_ca_path.empty() ?
nullptr : _http_ca_path.c_str());
164 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2);
165 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER,
true);
168 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);
171 curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, 100L * 1024L);
174 curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
177 if (!request->data.empty()) {
179 if (request->data.starts_with(
"{")) {
180 headers = curl_slist_append(headers,
"Content-Type: application/json");
182 headers = curl_slist_append(headers,
"Content-Type: application/x-www-form-urlencoded");
185 curl_easy_setopt(curl, CURLOPT_POST, 1L);
186 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request->data.c_str());
187 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
189 curl_easy_setopt(curl, CURLOPT_URL, request->uri.c_str());
192 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, +[](
char *ptr,
size_t size,
size_t nmemb,
void *userdata) ->
size_t {
193 Debug(net, 6,
"HTTP callback: {} bytes", size * nmemb);
197 std::unique_ptr<char[]> buffer = std::make_unique<char[]>(size * nmemb);
198 memcpy(buffer.get(), ptr, size * nmemb);
203 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &request->callback);
209 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
210 curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, +[](
void *userdata, curl_off_t , curl_off_t , curl_off_t , curl_off_t ) ->
int {
212 return (callback->cancelled || _http_thread_exit) ? 1 : 0;
214 curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &request->callback);
217 CURLcode res = curl_easy_perform(curl);
219 curl_slist_free_all(headers);
221 if (res == CURLE_OK) {
222 Debug(net, 1,
"HTTP request succeeded");
223 request->callback.OnReceiveData(
nullptr, 0);
225 long status_code = 0;
226 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status_code);
229 Debug(net, (request->callback.cancelled || _http_thread_exit || status_code == HTTP_429_TOO_MANY_REQUESTS) ? 1 : 0,
"HTTP request failed: status_code: {}, error: {}", status_code, curl_easy_strerror(res));
230 request->callback.OnFailure();
234 request->callback.WaitTillEmptyOrCondition([]() ->
bool {
235 return _http_thread_exit;
239 curl_easy_cleanup(curl);
244 curl_global_init(CURL_GLOBAL_DEFAULT);
250 for (
auto &ca_file : _certificate_files) {
252 _http_ca_file = ca_file;
256 if (_http_ca_file.empty()) {
257 for (
auto &ca_path : _certificate_directories) {
259 _http_ca_path = ca_path;
264 Debug(net, 3,
"Using certificate file: {}", _http_ca_file.empty() ?
"none" : _http_ca_file);
265 Debug(net, 3,
"Using certificate path: {}", _http_ca_path.empty() ?
"none" : _http_ca_path);
268 if (_http_ca_file.empty() && _http_ca_path.empty()) {
269 Debug(net, 0,
"No certificate files or directories found, HTTPS will not work!");
273 _http_thread_exit =
false;
279 _http_thread_exit =
true;
286 std::lock_guard<std::mutex>
lock(_http_mutex);
287 _http_cv.notify_one();
290 if (_http_thread.joinable()) {
294 curl_global_cleanup();