12#include "../../stdafx.h"
13#include "../../debug.h"
14#include "../../fileio_func.h"
16#include "../../thread.h"
17#include "../network_internal.h"
23#include <condition_variable>
28#include "../../safeguards.h"
32static 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",
41static auto _certificate_directories = {
44 "/system/etc/security/cacerts",
48static std::vector<HTTPThreadSafeCallback *> _http_callbacks;
49static std::vector<HTTPThreadSafeCallback *> _new_http_callbacks;
50static std::mutex _http_callback_mutex;
51static 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;
83static std::thread _http_thread;
84static std::atomic<bool> _http_thread_exit =
false;
85static std::queue<std::unique_ptr<NetworkHTTPRequest>> _http_requests;
86static std::mutex _http_mutex;
87static std::condition_variable _http_cv;
89static std::string _http_ca_file =
"";
90static 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();
Converts a HTTPCallback to a Thread-Safe variant.
void OnReceiveData(std::unique_ptr< char[]> data, size_t length)
Similar to HTTPCallback::OnReceiveData, but thread-safe.
NetworkHTTPRequest(const std::string &uri, HTTPCallback *callback, const std::string &data)
Create a new HTTP request.
const std::string data
Data to send, if any.
~NetworkHTTPRequest()
Destructor of the HTTP request.
HTTPThreadSafeCallback callback
Callback to send data back on.
const std::string uri
URI to connect to.
static void Connect(const std::string &uri, HTTPCallback *callback, const std::string data="")
Connect to the given URI.
static void HTTPReceive()
Do the receiving for all HTTP connections.
#define Debug(category, level, format_string,...)
Ouptut a line of debugging information.
bool FileExists(const std::string &filename)
Test whether the given filename exists.
Basic functions to send and receive HTTP packets.
void NetworkHTTPInitialize()
Initialize the HTTP socket handler.
void NetworkHTTPUninitialize()
Uninitialize the HTTP socket handler.
Shared functions for implementations of HTTP requests.
Callback for when the HTTP handler has something to tell us.
virtual void OnFailure()=0
An error has occurred and the connection has been closed.
bool StartNewThread(std::thread *thr, const char *name, TFn &&_Fx, TArgs &&... _Ax)
Start a new thread.
std::mutex lock
synchronization for playback status fields